Kumar
Kumar

Reputation: 741

Converting one hash to another hash in ruby

Convert the following hash to another hash.

{["2013-08-15", "123", "user1"]=>1, ["2013-08-15", "456", "user1"]=>1, ["2013-08-09", "789", "user1"]=>5} 

Convert the above hash to

{["2013-08-15", "user1"]=>2, ["2013-08-09", "user1"]=>1} 

As you can see the first and second key, value pairs in the hash have same date, different account, and same user, in this case i need to count the total number of user posts as two {["2013-08-15", "user1"]=>2}

in the last key, value pair, the count should be one because the user posted to only one account ("789") even though there are 5 posts {["2013-08-09", "user1"]=>1}.

Upvotes: 0

Views: 524

Answers (5)

tokland
tokland

Reputation: 67850

Using Facets' Enumerable#frequency you can write this clean and modular one-liner:

require 'facets'
hash1 = {["2013-08-15", "123", "user1"] => 1, ...}
hash2 = hash1.keys.map { |date, code, user| [date, user] }.frequency
#=> {["2013-08-15", "user1"] => 2, ["2013-08-09", "user1"] => 1}

Note that unpacking the values of the array makes easier to see what's going on. Also, note that you don't need to depend on Facets, just add Enumerable#frequency to your extensions library, it's a very common abstraction.

Upvotes: 3

Stefan
Stefan

Reputation: 114128

This can be done with group_by and map:

h = {["2013-08-15", "123", "user1"]=>1, ["2013-08-15", "456", "user1"]=>1, ["2013-08-09", "789", "user1"]=>5}

Hash[h.group_by { |(u, _, d), _| [u, d] }.map { |u, d| [u, d.size] }]
#=> {["2013-08-15", "user1"]=>2, ["2013-08-09", "user1"]=>1}

Upvotes: 1

Arup Rakshit
Arup Rakshit

Reputation: 118261

h = {["2013-08-15", "123", "user1"]=>1, ["2013-08-15", "456", "user1"]=>1, ["2013-08-09", "789","user1"]=>5} 
Hash[h.group_by{|k,v| k[0]}.map{|_,v| [v.flatten.values_at(0,2),v.size]}]
# => {["2013-08-15", "user1"]=>2, ["2013-08-09", "user1"]=>1}

Or,

h = {["2013-08-15", "123", "user1"]=>1, ["2013-08-15", "456", "user1"]=>1, ["2013-08-09", "789","user1"]=>5} 
h.each_with_object(Hash.new(0)){|((d,_,u),_),hsh| hsh[[d,u]] +=1 }
# => {["2013-08-15", "user1"]=>2, ["2013-08-09", "user1"]=>1}

Upvotes: 2

Abe Voelker
Abe Voelker

Reputation: 31564

Not super elegant but works. Also, using arrays as hash keys is kinda weird.

h = {["2013-08-15", "123", "user1"]=>1, ["2013-08-15", "456", "user1"]=>1, ["2013-08-09", "789", "user1"]=>5}
h.inject(Hash.new(0)){|a,((date,post,user),v)| a[[date,user]] +=1; a } # => {["2013-08-15", "user1"]=>2, ["2013-08-09", "user1"]=>1}

Upvotes: 2

Plasmarob
Plasmarob

Reputation: 1391

  hash1 = {["2013-08-15", "123", "user1"]=>1, ["2013-08-15", "456", "user1"]=>1, ["2013-08-09", "789", "user1"]=>5} 

  hash2 = Hash.new
  hash1.each do |x,y|
      hash2[ [x[0],x[2]] ] = hash2[ [x[0],x[2]] ].to_i + 1
  end
  puts hash2

Note that you need to use the .to_i method for first-time initialization, else we'd be adding with nil, but this makes it 0.

Upvotes: 1

Related Questions