how to map over the list of map and add them?

Viewed 323

I have a list of maps something like this

    iex(84)> a                                                            
[
  %{a: 1, b: 2, c: [%{x: 1, y: 2}, %{x: 2, y: 3}]},
  %{a: 3, b: 4, c: [%{x: 2, y: 3}, %{x: 2, y: 3}]},
  %{a: 5, b: 6, c: [%{x: 3, y: 4}, %{x: 3, y: 4}]}
]

I'm trying to get a result like this

  [
  %{a: 1, b: 2, c: 8},
  %{a: 3, b: 4, c: 10},
  %{a: 5, b: 6, c: 14}
]

not able to map over the values of "c" and if I do I'm losing the initial map. What should I do?

2 Answers

You can use a for/1 with map update syntax to replace the c key:

input = [
  %{a: 1, b: 2, c: [%{x: 1, y: 2}, %{x: 2, y: 3}]},
  %{a: 3, b: 4, c: [%{x: 2, y: 3}, %{x: 2, y: 3}]},
  %{a: 5, b: 6, c: [%{x: 3, y: 4}, %{x: 3, y: 4}]}
]

for map <- input do
  %{map | c: Enum.flat_map(map.c, &Map.values/1) |> Enum.sum()}
end

Output:

[%{a: 1, b: 2, c: 8}, %{a: 3, b: 4, c: 10}, %{a: 5, b: 6, c: 14}]

In the input we should modify deeply nested elements. There is the greatest extremely underrated way to deal with that: Access.

input = [
  %{a: 1, b: 2, c: [%{x: 1, y: 2}, %{x: 2, y: 3}]},
  %{a: 3, b: 4, c: [%{x: 2, y: 3}, %{x: 2, y: 3}]},
  %{a: 5, b: 6, c: [%{x: 3, y: 4}, %{x: 3, y: 4}]}
]

Here we would modify all the elements of the input, updating :c. So we need to get to [Access.all(), :c] and call update_in/3 on them.

update_in(input, [Access.all(), :c], fn x ->
  Enum.reduce(x, 0, & &2 + (&1 |> Map.values() |> Enum.sum()))
end)
#⇒ [%{a: 1, b: 2, c: 8},
#   %{a: 3, b: 4, c: 10},
#   %{a: 5, b: 6, c: 14}]

I believe the internal sum might be done in some more elegant manner, but that’s not the point. The point is: use Access here (and literally everywhere you operate on nested enumerables.)

Related