2

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.

2
  • 7
    The answer for these questions where you want to iterate and change something is always Enum.reduce. :) All of the other functions in the Enum module are implemented on top of it. I can post the complete answer but I think you would learn more if you tried to solve it Enum.reduce first. Commented Jan 22, 2018 at 9:37
  • Thanks! I was trying with reduce but couldnt figure out, I will keep trying, it's hard to think on functional way :P Commented Jan 22, 2018 at 9:46

2 Answers 2

13

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) 
Sign up to request clarification or add additional context in comments.

Comments

5

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.