通过Elixir中的多个值进行地图分组

时间:2019-04-17 13:47:00

标签: elixir

所以我有这张地图

    map = [
             %{id: 2, brand: "TUTU", reference: "1234"}, 
             %{id: 2, brand: "TUTU", reference: "4567"}, 
             %{id: 3, brand: "TOTO", reference: "789456"}
    ]

我想按ID将其分组以获得类似这样的内容:

    [
        %{
            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 个答案:

答案 0 :(得分:5)

另一种可能性是使用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()

答案 1 :(得分: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(&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"}最终位于不同的存储桶中。

答案 2 :(得分:1)

您还可以查看Enum.reduce/3Map.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