How improve this code? Taking keys and its value from a map based in the key

Hey guys, how can I improve this code?

I have a list of keys I will have to get with their values from a map and its sub-maps.

For example, I have the following map:
%{a: 1, b: 1, c: %{d: 1, e: 1}}

The result I need is:
%{a: 1, c: %{e: 1}}

I have it working now. I did something similar to the following code, but it does not look like the best way:

Map.take(name_map, [
  :a,
])
|> Map.put(
  :c,
  Map.take(name_map.c, [
	:e
  ])
)```

Thank you in advance!

It’s hard to make specific suggestions without more information about why some of the decisions are being made (why keep :a but not :b? why keep :e but not :d?), but some general thoughts:

  • combinations of Map.get (or some_map.key) and Map.put with the same key can usually be made clearer with Map.update
  • if you’re doing a lot of complex map manipulation, take a look at the Access module and supporting functions (get_in etc)
2 Likes

Since it wasn’t explicitly shown what this list of keys looks like, I came up with my own way of declaring the keys used to traverse a nested map.

defmodule Extracter do
  def extract(source, keys), do: extract(source, keys, %{})

  defp extract(_source, [], dest), do: dest

  defp extract(source, [{key, children} | rest], dest) do
    Map.put(extract(source, rest, dest), key, extract(source[key], children, %{}))
  end

  defp extract(source, [key | rest], dest) do
    Map.put(extract(source, rest, dest), key, source[key])
  end
end

source = %{a: 1, b: 1, c: %{d: 1, e: 1}}
keys = [:a, {:c, [:e]}]
Extracter.extract(source, keys)
# %{a: 1, c: %{e: 1}}

1 Like

That’s exactly what I was looking for.

Thank you, guys!