#elixir
#elixir
Вопрос:
Итак, у меня есть эта карта
map = [
%{id: 2, brand: "TUTU", reference: "1234"},
%{id: 2, brand: "TUTU", reference: "4567"},
%{id: 3, brand: "TOTO", reference: "789456"}
]
И я хотел бы сгруппировать ее по идентификатору, чтобы получить что-то вроде этого :
[
%{
id: 2,
brand: "TUTU",
reference: [
"1234",
"5845"
]
},
%{
id: 3,
brand: "TOTO",
reference: [
"4587"
]
}
]
Я пытался использовать Enum.group_by
так
map
|> Enum.group_by(fn entry -> entry.brand end)
Но результат выглядел так :
%{
"TOTO" => [%{brand: "TOTO", id: 3, reference: "789456"}],
"TUTU" => [
%{brand: "TUTU", id: 2, reference: "1234"},
%{brand: "TUTU", id: 2, reference: "4567"}
]
}
Я чувствую, что я близок к решению, но я не знаю, как я могу перегруппировать свою информацию в списки вместо того, чтобы использовать ключи в качестве индексов….
Извините, я не очень понимаю, я просто не знаю, как описать мою проблему
Ответ №1:
Другой возможностью было бы Enum.reduce/3
напрямую перейти к желаемой структуре с Kernel.update_in/3
:
data
|> Enum.reduce(%{}, fn %{id: id, brand: brand, reference: ref}, acc ->
update_in(acc, [{id, brand}], fn
nil -> %{id: id, brand: brand, reference: [ref]}
refs -> %{refs | reference: [ref | refs.reference]}
end)
end)
|> Map.values()
Ответ №2:
Если вам не нужно быть очень изобретательным в обнаружении ключей, которые не нужно группировать, и вы можете просто перечислить их заранее, то вложенный Enum.map
, подобный этому, будет делать то, что вы хотите:
data = [
%{id: 2, brand: "TUTU", reference: "1234"},
%{id: 2, brand: "TUTU", reference: "4567"},
%{id: 2, brand: "TOTO", reference: "4567"},
%{id: 3, brand: "TOTO", reference: "789456"}
]
keys_to_group_by = [:id, :brand]
keys_to_list = [:reference]
data
|> Enum.group_by(amp;Map.take(amp;1, keys_to_group_by))
|> Enum.map(fn {key, values} ->
keys_to_list
|> Enum.map(fn key_to_list ->
{key_to_list, Enum.map(values, amp; amp;1[key_to_list])}
end)
|> Enum.into(key)
end)
Обратите |> Enum.group_by(amp;Map.take(amp;1, keys_to_group_by))
внимание — это часть, которая группируется по многим ключам, так что %{id: 2, brand: "TOTO"}
и %{id: 2, brand: "TUTU"}
попадают в разные сегменты.
Ответ №3:
Вы также можете взглянуть на Enum.reduce/3 и Map.update/4 вместо простого Enum.group_by/2
, чтобы избавить вас от необходимости затем сопоставлять сгруппированные записи только со ссылками.
map
|> Enum.reduce(%{}, amp;group_by_brand/2)
|> Enum.map(amp;to_map/1)
...
defp group_by_brand(entry, acc) do
Map.update(acc, {entry.id, entry.brand}, [entry.reference], amp;[entry.reference | amp;1])
end
defp to_map({{id, brand}, references}) do
%{id: id, brand: brand, references: references}
end