lapinkoira
lapinkoira

Reputation: 9008

Convert list of maps into one single map

How could a list of maps like some_maps = [%{"test" => [1]}, %{"test2" => [2]}, %{"test" => [3]}] be converted to one huge single map with it's values merged?

single_map = %{"test" => [1, 3], "test2" => [2]}

Since I cannot modify a map within an iteration like for I dont know how to build this map

In other language I would define an empty map and iterate through the list and fill the map but functionally thinking I dont see how I could do this.

Upvotes: 2

Views: 3950

Answers (2)

Chris Meyer
Chris Meyer

Reputation: 1631

The reduce solution is definitely the production-quality answer. However, since you mentioned the difficulty you are having with functional programming, consider the 'long-hand' version of the reduce:

defmodule MapMerger do
  # The api function takes a list of maps, and returns them merged together.
  def merge(list_of_maps) do
    # This is written with a second function to hopefully be easier to follow;
    # these two functions could be collapsed using a default parameter
    # for the accumulator.
    do_merge(list_of_maps, %{})
  end

  # This is the base case, which will match after all maps have been processed
  # and the list is empty:
  defp do_merge([], acc), do: acc

  # Next comes the actual iterator; we pull head (the first item in the list),
  # process it, then recurse on the rest of the list and an updated accumulator
  defp do_merge([head|rest], acc) do
    updated_acc = Map.merge(acc, head)
    do_merge(rest, updated_acc)
  end
end

Once you can follow this, reduce should easier to think about- it doesn't modify anything, it just keeps recursing with new parameters that happen to be updated versions of the old parameters. My production code usually uses reduce for small work like this, but when the operation inside the reduce is complicated, I usually decompose the reduce to a proper function that is easier to reason about, and easier to mark up with comments.

From your original question:

In other language I would define an empty map and iterate through the list and fill the map

Note that this is a reasonable description of how the merge and do_merge functions above work. You aren't as far away from thinking functionally as you believe.

Upvotes: 5

helios35
helios35

Reputation: 1637

Here's one way to do it:

Enum.reduce(some_maps, fn x, y ->
   Map.merge(x, y, fn _k, v1, v2 -> v2 ++ v1 end)
end)

Upvotes: 13

Related Questions