Kernel.get_and_update_in
get_and_update_in
, go back to Kernel module for more information.
Specs
get_and_update_in( structure, keys, (term() | nil -> {current_value, new_value} | :pop) ) :: {current_value, new_structure :: structure} when structure: Access.t(), keys: [any(), ...], current_value: Access.value(), new_value: Access.value()
Gets a value and updates a nested structure.
data
is a nested structure (that is, a map, keyword
list, or struct that implements the Access
behaviour).
The fun
argument receives the value of key
(or nil
if key
is not present) and must return one of the following values:
a two-element tuple
{current_value, new_value}
. In this case,current_value
is the retrieved value which can possibly be operated on before being returned.new_value
is the new value to be stored underkey
.:pop
, which implies that the current value underkey
should be removed from the structure and returned.
This function uses the Access
module to traverse the structures
according to the given keys
, unless the key
is a function,
which is detailed in a later section.
Examples
This function is useful when there is a need to retrieve the current value (or something calculated in function of the current value) and update it at the same time. For example, it could be used to read the current age of a user while increasing it by one in one pass:
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}
Functions as keys
If a key is a function, the function will be invoked passing three arguments:
- the operation (
:get_and_update
) - the data to be accessed
- a function to be invoked next
This means get_and_update_in/3
can be extended to provide custom
lookups. The downside is that functions cannot be stored as keys
in the accessed data structures.
When one of the keys is a function, the function is invoked. In the example below, we use a function to get and increment all ages inside a list:
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get_and_update, data, next ->
...> data |> Enum.map(next) |> Enum.unzip()
...> end
iex> get_and_update_in(users, [all, :age], &{&1, &1 + 1})
{[27, 23], [%{name: "john", age: 28}, %{name: "meg", age: 24}]}
If the previous value before invoking the function is nil
,
the function will receive nil
as a value and must handle it
accordingly (be it by failing or providing a sane default).
The Access
module ships with many convenience accessor functions,
like the all
anonymous function defined above. See Access.all/0
,
Access.key/2
, and others as examples.