Marek Radebeul
Marek Radebeul

Reputation: 45

How do I add sums per group in two-dimensional array using enumerable functions?

I have a two-dimensional array as follows:

ary = [["a", 10],["a", 20],["b", 9],["b",7],["c",12]]

I want to sum the numeric values with the same key, building a hash like:

desired_result = {"a"=>30, "b"=>16, "c"=>12}

I can use a hash with a default (0) and a loop as follows:

rslt = Hash.new(0)
ary.each do |line|
  rslt[line[0]] += line[1]
end

But I want to avoid the loop and use enumeration functions. I came up with the following (quite ugly) expression:

rslt = ary.group_by {|a| a[0]}.map {|k,v| [k, v.map {|v| v[1]}.reduce(:+)]}.to_h

which is much harder to read than the loop-version.

Is there a way to do this more elegantly without a loop?

Upvotes: 1

Views: 65

Answers (4)

iGian
iGian

Reputation: 11193

After Enumerable#group_by I'd suggest to chain Hash#transform_values:

ary.group_by(&:first).transform_values { |v| v.sum(&:last) }
#=> {"a"=>30, "b"=>16, "c"=>12}

Upvotes: 2

Sajad Rastegar
Sajad Rastegar

Reputation: 3154

ary.group_by(&:first).map {|k, v| [k, v.sum(&:last)]}.to_h

Upvotes: 1

jerhinesmith
jerhinesmith

Reputation: 15492

You could use each_with_object (or inject) and pass in a hash where new keys get initialized with a value of zero:

ary.each_with_object(Hash.new(0)){ |(k, v), count| count[k] += v }
# => {"a"=>30, "b"=>16, "c"=>12}

Upvotes: 3

Rajagopalan
Rajagopalan

Reputation: 6064

ary = [["a", 10],["a", 20],["b", 9],["b",7],["c",12]]

h=ary.group_by{|x|x[0]}.each_with_object({}) do |(k,v),h|
  h[k]=v.sum{|y|y[1]}
end

p h

{"a"=>30, "b"=>16, "c"=>12}

Upvotes: 1

Related Questions