Update a 2-level-nested map

I have a 2-level-nested map, how can I update each value on the 2nd level? Right now I’m doing this:

      items = Enum.map(items, fn(a) ->
        a.items2 = Enum.map(a.items2, fn(a2) ->
          Map.put(x2, :new_value, 123) 
        end)

        a
      end)

An error:

cannot invoke remote function "a.items2/0" inside match. 

I basically know what this means, but how to fix it?

Maybe put_in can help?
https://hexdocs.pm/elixir/Kernel.html#put_in/3

2 Likes

The reason for the error you get is that on the second line of your code, you attempt to fill some value into a.items2. However, it is impossible to do structure.property = new_value in Elixir. Rather,

new_structure = put_in(structure.property, new_value)

or

new_structure = put_in(structure, :property, new_value)

is used.

Your code then becomes:

items = Enum.map(items, fn item ->
  put_in(item.items2, Enum.map(item.items2, fn item2) -> 
    Map.put(a2, :new_value, 123)
  end)
end)

To make this more readable:

def main_loop(items) do
  Enum.map(items, &alter_item/1)
end

def alter_item(item = %{items2: nested_items) do
  new_nested_items = Enum.map(nested_items, &alter_nested_item/1)
  %{item | items2: new_nested_items}
end

def alter_nested_item(nested_item) do
  Map.put(nested_item, :new_value, 123)
end

Note that because of how this is split up in separate functions, the put_in becomes superfluous since pattern matching is used.