Карта группы по нескольким значениям в эликсире - PullRequest
0 голосов
/ 17 апреля 2019

Итак, у меня есть эта карта

    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"}
      ]
    }

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

Извините, я не очень ясно, я просто не знаю, как описать мою проблему

Ответы [ 3 ]

5 голосов
/ 18 апреля 2019

Другой возможностью было бы 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 голосов
/ 17 апреля 2019

Если вам не нужно слишком увлекаться поиском ключей, по которым нет необходимости группировать их, и можете просто перечислить их заранее, то вложенный 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(&Map.take(&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, & &1[key_to_list])}
  end)
  |> Enum.into(key)
end)

Обратите внимание на |> Enum.group_by(&Map.take(&1, keys_to_group_by)) - это та часть, которая группируется по множеству ключей, так что %{id: 2, brand: "TOTO"} и %{id: 2, brand: "TUTU"} заканчиваются в разных сегментах.

1 голос
/ 17 апреля 2019

Вы также можете взглянуть на Enum.reduce / 3 и Map.update / 4 вместо простого Enum.group_by/2, чтобы избавить вас от необходимости затем отображать сгруппированные записи только в ссылках.

map
|> Enum.reduce(%{}, &group_by_brand/2)
|> Enum.map(&to_map/1)

...

defp group_by_brand(entry, acc) do
  Map.update(acc, {entry.id, entry.brand}, [entry.reference], &[entry.reference | &1])
end

defp to_map({{id, brand}, references}) do
  %{id: id, brand: brand, references: references}
end
...