Kernel.update_in makes it easy to update nested structures. For example, let's say we want to change Jack's last name to "Nimble" in the following list of maps:
list=[
%{name: %{first: "Jack", last: "Munroe"}, age: 30},
%{name: %{first: "Jill", last: "Valentine"}, age: 23}
]
In languages with mutability like Python, Java or Go, we could use something like list[0].name.last="Nimble"
. But data is immutable in Elixir, so a similar approach won't work (Enum.at(list, 0).name.last = "Nimble"
will give an error). Instead, we can use a combination of Map.update! and List.update_at:
List.update_at(list, 0, fn
jack -> Map.update!(jack, :name, fn
name -> %{name | last: "Nimble"}
end)
end)
Here, we had to tell Elixir how to get to the next level in the structure and update it.
First, the first map in the list.
Then getting
name
map within that.Finally, updating
last
to"Nimble"
.
That's quite a few steps we had to cover! Kernel.update_in makes this easier by letting us specifying a path to field. For example:
update_in(list, [Access.at(0), :name, :last], fn _ -> "Nimble" end)
The second argument is the path (as a list) telling Elixir how to get to field we want to update. The function will traverse the structure according to this path. Access.at(0)
actually returns a function telling update_in
to go into the first element in the list. You'll need to use this to go inside a list. The rest are atoms specifying the keys of the map to go to.
Kernel.update_in can also update a list within a map. For example, changing the last element of a list inside a map:
iex> the_map=%{fruits: ["apple", "banana", "orange"]}
%{fruits: ["apple", "banana", "orange"]}
iex> update_in(the_map, [:fruits, Access.at(2)], fn _ -> "plum" end)
%{fruits: ["apple", "banana", "plum"]}
By providing a generic way of going through a structure and updating, Kernel.update_in makes it easy to update something in nested data structure. Hope you find this tip useful!