Sonny Parlin
Sonny Parlin

Reputation: 931

Convert Array to Hash removing duplicate keys and adding values at the same time

I have an array that looks like so:

f = [["Wed, 12-31", 120.0],["Thu, 01-01", 120.0], ["Thu, 01-01", 120.0]]

I can convert it to a hash and remove the duplicate keys:

h = Hash[ *f.collect { |v| [v] }.flatten ]
# => {"Wed, 12-31"=>120.0, "Thu, 01-01"=>120.0}

which is almost there, but I'd like to sum the value for elements with the identical date strings, the desired result from the above array would be this:

{"Wed, 12-31"=>120.0, "Thu, 01-01"=>240.0}

How can I accomplish this?

Upvotes: 1

Views: 635

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110675

The other standard approach to this type of problem is to use Enumerable#group_by:

Hash[ary.group_by(&:first).map { |d,a| [d, a.reduce(0) { |t,(_,n)| t+n }] }]
  #=> {"Wed, 12-31"=>120.0, "Thu, 01-01"=>240.0}

We have:

a = ary.group_by(&:first)
  #=> {"Wed, 12-31"=>[["Wed, 12-31", 120.0]],
  #    "Thu, 01-01"=>[["Thu, 01-01", 120.0], ["Thu, 01-01", 120.0]]} 
b = a.map { |d,a| [d, a.reduce(0) { |t,(_,n)| t+n }] }
  #=> [["Wed, 12-31", 120.0], ["Thu, 01-01", 240.0]] 
Hash[b] 
  #=> {"Wed, 12-31"=>120.0, "Thu, 01-01"=>240.0} 

or with Ruby 2.0+:

b.to_h
  #=> {"Wed, 12-31"=>120.0, "Thu, 01-01"=>240.0} 

A variant of this is:

ary.group_by(&:first).map { |d,a| [d, a.transpose.last.reduce(:+) ] }.to_h

Upvotes: 2

the Tin Man
the Tin Man

Reputation: 160551

I'd use each_with_object:

ary = [["Wed, 12-31", 120.0], ["Thu, 01-01", 120.0], ["Thu, 01-01", 120.0]]

hash = ary.each_with_object(Hash.new(0)){ |(k,v), h| h[k] += v }
=> {"Wed, 12-31"=>120.0, "Thu, 01-01"=>240.0}

Upvotes: 2

Kenny Meyer
Kenny Meyer

Reputation: 8027

This works:

result = Hash.new(0)
f = [["Wed, 12-31", 120.0],["Thu, 01-01", 120.0], ["Thu, 01-01", 120.0]]
f.each { |subarray| result[subarray[0]] += subarray[1] }
puts result

If you would like to be fancy you could use .inject()

Upvotes: 2

Related Questions