Map over a map and create a new map?

Hello! I’m trying to get more comfy with idiomatic elixir, and it seems like one line anonymous fn {} -> ... end calls might not be ideal. Maybe they are, but I’m curious if there’s a cleaner way to do a map over a map and get another map.

Here’s what I have:, fn {k,v}-> {k, length(v)} end) |>

Is there a way to write this more concisely? At first I thought capture syntax might be the way, but I can’t find a function to capture that creates a tuple.

And for bonus points, can you destructure capture arguments? Because I also have one of these guys:
|> Enum.flat_map(fn {_, %{metas: metas}} -> metas end)

For the second example, I came up with this (which does indeed make me happier):

|> Map.values
|> Enum.flat_map(&Map.get(&1, :metas))
1 Like _k, v -> length(v) end, m)

maybe not idiomatic, but erlang library comes in handy here.

1 Like

I might use for:

for {k, v} <- m, do: {k, length(v)}, into: %{}


What about skipping the first, & {elem(&1, 0), String.length(elem(&1, 1))})

You can also use for with reduce.

for {k, v} <- m, reduce: %{} do acc -> Map.put(acc, k, String.length(v)) end

or Enum.reduce directly

Enum.reduce(m, %{}, fn {k, v}, acc -> ... end)


Enum.into(m, %{}, fn {k,v} -> {k,length(v)} end)
Enum.into(%{a: [1], b: [1,2]}, %{}, fn {k,l} -> {k,length(l)} end)
1 Like

Actually accepts a function, so you can also directly call, fn {k,v}-> {k, length(v)} end).


It took 5 replies to get the most straight solution for such a simple question. :smiley: I believe normally people would write what mrkurt already wrote which is not “not concise” at all.

You might not have seen I used in my reply :slight_smile:


Oh sorry I missed it!

Also worth noting, should be the fastest option as well according to this benchmark.



Oh my gosh these are great. That function was exactly what I was looking for. My brain seems to handle the inline function better than complex capture.


Here’ s reply number 6.

Enum.into(m, %{}, fn {k,v}-> {k, length(v)} end)

This would have been my solution, though I would have put the into: before the do:

If this was immediately following a call to group_by/3, you can use frequencies/1 or frequencies_by/2 instead:

[:a, :a, :b, :b, :b, :c, :c, :c, :c]
|> Enum.group_by(& &1)
|> {k, v} -> {k, length(v)} end)

[:a, :a, :b, :b, :b, :c, :c, :c, :c]
|> Enum.frequencies()

Result for both: %{a: 2, b: 3, c: 4}

1 Like