# Return a grouped sum from a list

I just started with Elixir and this is what I have now. Is there an easier or better way to write this?

defmodule GroupBySum do
def sum(col), do: sum(%{}, col)
defp sum(r, [h|t]) do
{k, v} = h
r = r |> Dict.update(k, v, fn(val) -> val + v end)
sum(r, t)
end
defp sum(r, []), do: r
end

list = [a: 1, b: 2, c: 3, a: 2, b: 1, c: 0]
IO.inspect GroupBySum.sum(list) #%{a: 3, b: 3, c: 3}

• Enum.reduce/3 is a more general wrapper for the recursion pattern you've implemented: elixir-lang.org/docs/v1.1/elixir/Enumerable.html#reduce/3 Also it might be more idiomatic to avoid needing the h variable by moving the {k, v} destructuring right into the parameters. – alxndr Sep 29 '15 at 17:06

It can be done several ways. Instead of explicitly using recursion we can, as @alxndr suggested, make use of the recursion capabilities of the Enum module. I am going to use Enum.map_reduce simply because I find it useful when troubleshooting to key the items and the accumulator in that form.

In general the solution is the same as yours.

  def sum(list) do
list |> Enum.map_reduce([],
fn({key, val}, acc) ->
{{key, val}, Keyword.update(acc, key, val, &(&1 + val))}
end
) |> elem(1)
end


The anonmous function in map_reduce accepts and item and an accumulator. Since the item is a keyword list, it has the form {key,val} so I match that in the function definition. The return from this function must be a tuple with the accumulator as the second value. For testing purposes I returned the same {key, val} as the first item. The Keyword.update function is the accumulator. A form of this same routine that helps the intermediate values be visible is:

def sum(list) do
list |> Enum.map_reduce([],
fn({key, val}, acc) ->
x = Keyword.update(acc, key, val, &(&1 + val))}
IO.inspect(x)
{{key, val}, x}
end
) |> elem(1)
end


In the case of the input you provided, the IO.inspect helps me understand how the routine collected the desired result, as seen below.

[a: 1]
[a: 1, b: 2]
[a: 1, b: 2, c: 3]
[a: 3, b: 2, c: 3]
[a: 3, b: 3, c: 3]
[a: 3, b: 3, c: 3]
[a: 3, b: 3, c: 3]


I found this a fun exercise -- thanks for posting.

• Welcome to CodeReview :) A fine and in-depth first answer, I hope you'll stick around! – Caridorc Dec 20 '15 at 11:00