Подтвердить что ты не робот

Получить конкретные значения ключей в списке карт с помощью Elixir?

Будучи новичком в Elixir, у меня возникают проблемы с пониманием соответствия шаблонов.

Если у меня есть структура данных Elixir:

list_with_maps = [%{"id" => 1, "name" => "a"}, %{"id" => 2, "name" => "b"}]

Каким будет лучший способ получить значения всех полей id из него?

4b9b3361

Ответ 1

Вы можете отобразить список и вернуть его с помощью Enum.map/2

Enum.map(list_with_maps, fn (x) -> x["id"] end)
[1, 2]

Вы можете написать ту же функцию с помощью оператора capture:

Enum.map(list_with_maps, & &1["id"])

Я предпочитаю писать & &1["id"] как &(&1["id"]), но скобки необязательны.

Ответ 2

Более общий (и более простой) способ получения подмножества ключей Map состоит в использовании Map.take/2, который вы можете использовать следующим образом:

map = %{"id" => 1, "name" => "a"}
Map.take(map, ["id"])
> %{"id" => 1}

Как вы можете видеть, он принимает массив ключей и возвращает новую карту только с теми ключами, которые вы хотите.

Теперь, применяя это к списку так же просто, как с помощью карты, а затем используя Map.take/2 в функции mapper. Как было указано, вы можете сделать это, используя либо лямбда:

Enum.map(list_with_maps, fn (map) -> Map.take(map, ["id"]) end)

Или вы можете использовать захват:

Enum.map(list_with_maps, &(Map.take(&1, ["id"])))

Это создаст более промежуточные карты, но для большинства ситуаций, которые не будут проблемой, поскольку Elixir довольно умен в отношении повторного использования памяти и на самом деле не будет создавать эти объекты много раз, unles

Ответ 3

Для полноты ответов на этот вопрос вы также можете сделать что-то вроде этого:

defmodule Test do
  def get_all_ids([head | tail ]) do
    IO.puts head["id"]
    get_all_ids(tail)
  end

  def get_all_ids([]) do
    IO.puts "end"
  end
end

Что будет использоваться так:

iex(7)> Test.get_all_ids(list_with_maps)
1
2
end
:ok

Хотя я думаю, что ответ Gazler - лучший ответ в этом случае.

О, и поскольку вы специально упомянули соответствие шаблонов, это также сработает:

defmodule Test do
  def get_all_ids([%{"id" => id} = m  | tail ]) do
    IO.puts id
    get_all_ids(tail)
  end

  def get_all_ids([]) do
    IO.puts "end"
  end
end

Вызов будет точно таким же; разница во втором подходе заключается в том, что он использует совпадение шаблона для анализа карты в списке аргументов.

Вы также можете изменить список аргументов в этой строке: def get_all_ids([%{"id" => id} = m | tail ]) do: def get_all_ids([%{"id" => id} = _m | tail ]) do, чтобы избежать предупреждения о m, который не используется.

Ответ 4

Чтобы извлечь имя из списка карт

names = for %{name: n, id: _} <- list_with_maps, do: n

Ответ 5

Другой вариант - использовать функцию get_in и использовать модуль Access. Модуль Access позволяет создавать выразительные способы работы с вложенными структурами данных.

Чтобы получить список значений в "id" вы можете запустить:

get_in(list_with_maps, [Access.all, "id"])  

Передача Access.all в качестве первого элемента в списке с последующим "id" говорит get_in возвращать все list_with_maps "id" list_with_maps.

Для этого примера это может показаться излишним, но если ваша структура выглядела так:

list_with_maps = [%{"id" => 1, "identities" => [%{"name" => "a"}, %{"name" => "Secret A"}]}, %{"id" => 2, "identities" => [%{"name" => "b"},%{"name" => "Secret B"}]}]

Мы могли бы получить все имена с:

get_in(list_with_maps, [Access.all, "identities", Access.all, "name"])
# [["a", "Secret A"], ["b", "Secret B"]]