Combine values of a list of Map by a specific field

Hello everyone,

I need to combine a list of Maps into a shorter list of maps, using a specific value as aggregator and key as conditional.

Basically, I need to turn this

[
    %{
        id: 1,
        value: [1]
    },
    %{
        id: 2,
        value: [1]
    },
    %{
        id: 2,
        value: [3]
    },
    %{
        id: 3,
        value: [1]
    },
    %{
        id: 2,
        value: [4]
    },
    %{
        id: 3,
        value: [2]
    }
]

into this

[
    %{
        id: 1,
        value: [1]
    },
    %{
        id: 2,
        value: [1, 3, 4]
    },
    %{
        id: 3,
        value: [1, 2]
    }
]

I’ve achieved this result using Map.merge, passing two specific items of those lists:

Map.merge(a, b, fn x, v1, v2 ->
  case x do
    :value -> Enum.concat(v1, v2)
    _ -> v1
  end
end)

But I’m unable to replicate this to a list of Maps.
I’ve imagined using Enum.reduce, or Enum.group_by, but nothing seems to work.

Could anyone help me, please?

1 Like

group_by is the easiest way to start, though it could be solved more efficiently with reduce

input
|> Enum.group_by(& &1.id)
|> Enum.map(fn {id, list} -> %{id => Enum.flat_map(list, & &1.value)} end)

edit: The above isn’t quite right
Correction:

input
|> Enum.group_by(& &1.id)
|> Enum.map(fn {id, list} -> %{id: id, value: Enum.flat_map(list, & &1.value)} end)
1 Like

Thanks, that’s exactly what I’m looking for