[Proposal] Add Access.values/0

70 views
Skip to first unread message

David Pavlík

unread,
Mar 18, 2025, 10:03:52 AMMar 18
to elixir-lang-core
Hi!

I've recently discovered that get_and_update_in/3 and friends can access multiple items at once via Access.all/0, Access.filter/1, and similar. It's a game changer for accessing deeply nested structures.

However, there is no function in the standard library for accessing multiple items of a map (similarly to items of a list). For maps where keys are something item IDs and not predefined atoms, I think such accessors would be very useful. In our codebase, for example, we often manipulate deeply nested structures that include such maps and want to access all values in such maps (e.g., when migrating data).

It's easy to write an accessor that accesses all values in a map (and similarly for keys or both keys and values):

@doc """
Returns a function that accesses all values in a map.

The returned function is typically passed as an accessor to `Kernel.get_in/2`,
`Kernel.get_and_update_in/3`, and friends.

## Examples

    iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
    iex> get_in(users, [AccessUtils.values(), :age])
    [27, 23]
    iex> update_in(users, [AccessUtils.values(), :age], fn age -> age + 1 end)
    %{"john" => %{age: 28}, "meg" => %{age: 24}}
    iex> put_in(users, [AccessUtils.values(), :phone], nil)
    %{"john" => %{age: 27, phone: nil}, "meg" => %{age: 23, phone: nil}}

"""
@spec values() :: Access.access_fun(data :: map(), current_value :: list())
def values do
  fn
    :get, %{} = data, next ->
      Enum.map(data, fn {_key, value} -> next.(value) end)

    :get_and_update, %{} = data, next ->
      {reverse_gets, updated_data} =
        Enum.reduce(data, {[], %{}}, fn {key, value}, {gets, data_acc} ->
          case next.(value) do
            {get, update} -> {[get | gets], Map.put(data_acc, key, update)}
            :pop -> {[value | gets], data_acc}
          end
        end)

      {Enum.reverse(reverse_gets), updated_data}
  end
end


Nevertheless, I think it might be useful to include it in the Access module of the standard library. What do you think?

Note that something similar has already been discussed on Elixir Forum.

Best regards,
David

José Valim

unread,
Mar 19, 2025, 11:51:20 AMMar 19
to elixir-l...@googlegroups.com
Please do submit a pull request. It should be similar to "all/0" except it expects a map instead (and then traverses only values). Thank you!


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/elixir-lang-core/847b6461-aaf4-4d2c-abbd-6b2ddee73b21n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages