[Proposal] Access.take, Access.keys

63 views
Skip to first unread message

Joe Martinez

unread,
Jul 25, 2023, 6:23:35 PM7/25/23
to elixir-lang-core
I have a feeling there's probably a better way to do this, but I couldn't find it in the history.

This would let you do something like this:
iex> users = [%{name: "john", age: 27, employment: %{title: "Developer", salary: 100}}, %{name: "meg", age: 23, employment: %{title: "Manager", salary: 200}}]
iex> get_in(users, [Access.all(), Access.keys([:name, employment: [:title]])])
[%{employment: %{title: "Developer"}, name: "john"}, %{name: "meg", employment: %{title: "Manager"}}]

With keys partially implemented like this
def keys(keys) do
  fn
    :get, data, next ->
      next.(take(data, keys))

    :get_and_update, data, next ->
      values = take(data, keys)

      case next.(values) do
        {values, :pop} -> {values, Map.drop(data, keys)}
        {values, updates} -> {values, updates}
      end
  end
end

And then that `take` function something like this:
@doc """
## Examples
    iex> Access.take(%{name: "john", age: 52}, [:name, :age, :height])
    %{name: "john", age: 52}

    iex> Access.take(%{name: "john", age: 52, employment: %{title: "Developer", salary: 100}}, [:name, :height, employment: [:title]])
    %{name: "john", employment: %{title: "Developer"}}
"""
def take(container, keys) when is_list(keys) do
  cond do
    Enum.all?(keys, &is_atom/1) and is_map(container) ->
      Map.take(container, keys)

    is_map(container) ->
      keys
      |> Enum.group_by(&is_atom/1)
      |> Enum.reduce(%{}, fn
        {true, keys}, map ->
          Map.merge(map, Map.take(container, keys))

        {false, pairs}, map ->
          Map.merge(
            map,
            for({k, v} <- pairs, into: %{}, do: {k, take(Map.get(container, k), v)})
          )
      end)
  end
end

def take(_container, keys) when is_list(keys) do
  raise ArgumentError, "Access.take expects the keys to be a list of atoms or keyword list, got: " <> inspect(keys)
end

Joe Martinez

unread,
Jul 25, 2023, 6:43:35 PM7/25/23
to elixir-lang-core
Nevermind, I kept researching, and while I didn't find the same exact proposal, I did encounter things down the rabbit hole that changed my mind.

I think Andrea's criticism of short_maps(https://andrealeopardi.com/posts/a-story-of-regret-and-retiring-a-library-from-hex/#why-i-don-t-like-short-maps) applies here too. Gonna shortcut the year of learning, accept his wisdom, and withdraw.

Reply all
Reply to author
Forward
0 new messages