HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Return a grouped sum from a list

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
returngroupedsumlistfrom

Problem

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}

Solution

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.

Code Snippets

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
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
[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]

Context

StackExchange Code Review Q#105881, answer score: 5

Revisions (0)

No revisions yet.