1
votes

What's a good way to map and reduce the elixir's list and convert it into new list.

Requirement: 1. Find the maps with same id: 2. Merge the values of "role" key (i.e. Gather all the unique values). 3. For all other maps(elements of list), do nothing.

list = [%{"id": 1, "role": ["A", "B"]}, %{"id": 2, "role": ["B", "C"]}, %{"id": 1, "role": ["C", "A"]} ]

needs to be converted in the following list:

ans_list = [%{"id": 1, "role": ["A", "B", "C"]}, %{"id": 2, "role": ["B", "C"]}]
1

1 Answers

5
votes

You can use Enum.group_by/2 to group by the id, then for each group, pass the role to Enum.flat_map/2 and Enum.uniq/1:

list = [%{"id": 1, "role": ["A", "B"]}, %{"id": 2, "role": ["B", "C"]}, %{"id": 1, "role": ["C", "A"]} ]

list
|> Enum.group_by(&(&1.id))
|> Enum.map(fn {key, value} ->
  %{id: key, role: value |> Enum.flat_map(&(&1.role)) |> Enum.uniq}
end)
|> IO.inspect

Output:

[%{id: 1, role: ["A", "B", "C"]}, %{id: 2, role: ["B", "C"]}]

As requested in a comment below, here's how to preserve all key/value pairs and only modify role of the first item of a group:

list =  [%{"id": 1, "role": ["A", "B"], "somekey": "value of the key 1"},
         %{"id": 2, "role": ["B", "C"], "somekey": "value of the key 2"},
         %{"id": 1, "role": ["C", "A"], "somekey": "value of the key 3"}]

list
|> Enum.group_by(&(&1.id))
|> Enum.map(fn {_, [value | _] = values} ->
  %{value | role: values |> Enum.flat_map(&(&1.role)) |> Enum.uniq}
end)
|> IO.inspect

Output:

[%{id: 1, role: ["A", "B", "C"], somekey: "value of the key 1"},
 %{id: 2, role: ["B", "C"], somekey: "value of the key 2"}]