Reputation: 45
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
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
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
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