get_in для вложенного списка и структуры в elixir

#elixir

#elixir

Вопрос:

У меня есть структура

 s = [
  a: %Bla{
   b: "c"
  }
]
  

Я хочу извлечь c из него значение. Я пытаюсь сделать

 get_in(s, [:a, :b])
  

Но он не предназначен для получения значения из структуры. Есть ли какой-либо аналог, который позволяет мне извлекать c данные из списка с помощью вложенной структуры?

Комментарии:

1. get_in(s, [:a, :b]) #=> "c" для меня.

2. Дерьмо! Позвольте мне выяснить, почему это не работает на моей стороне

3. @Dogbert обновил вопрос. Структура имеет значение!

Ответ №1:

Как задокументировано, get_in по умолчанию не работает со структурами:

Синтаксис доступа (foo[bar]) не может использоваться для доступа к полям в структурах, поскольку структуры не реализуют поведение доступа по умолчанию. Это также дизайнерское решение: динамический поиск доступа предназначен для динамических структур ключ-значение, таких как карты и ключевые слова, а не для статических, таких как структуры.

Есть два способа добиться того, чего вы хотите:

  1. Реализуйте Access поведение для вашей структуры.

  2. Используйте Access.key(:foo) вместо :foo .

Я бы использовал (2):

 iex(1)> defmodule Bla do
...(1)>   defstruct [:b]
...(1)> end
iex(2)> s = [a: %Bla{b: "c"}]
[a: %Bla{b: "c"}]
iex(3)> get_in(s, [:a, Access.key(:b)])
"c"
  

Комментарии:

1. Access.key(:foo) означает, что если вы используете get_in(nil, [Access.key(:foo)]) его, он выдаст ошибку, а не вернет nil 🙁

Ответ №2:

Вот моя версия try функции для возврата значений как из maps, так и из structs:

 def try(map, keys) do
  Enum.reduce(keys, map, fn key, acc -> 
    if acc, do: Map.get(acc, key) 
  end)
end
  

Ответ №3:

Это мой ответ, который более снисходителен к встречающимся значениям, отличным от карты, таким как значение в середине обхода, равное нулю, строка или что-то еще Map.get() , на что можно пожаловаться

По сути, это более щадящая версия Kernel.get_in/2 . Если какой-либо ключ отсутствует, или если в середине обхода он получает не отображаемую вещь, он вернет nil .

     @spec key_getter(map(), list(atom() | String.t())) :: any() | nil
    def key_getter(nil, _), do: nil
    def key_getter(map_or_struct, []), do: map_or_struct
    def key_getter(map_or_struct, _) when not is_map(map_or_struct), do: nil

    def key_getter(map_or_struct, [next_key | keys]),
      do: key_getter(Map.get(map_or_struct, next_key, nil), keys)