tech_human
tech_human

Reputation: 7100

Creating nested hash in ruby or use of group_by

I have an array as follows:

my_array = [
  [1426049999000, "Apple", 2.7235],
  [1426049999000, "Orange", 2.942],
  [1424235599000, "Apple", 1.124],
  [1424235599000, "Orange", 1.115]
]

I want either a hash of hashes:

{
  :"Apple" => {1426049999000 => 2.7235, 1424235599000 => 1.124},
  :"Orange" => {1426049999000 => 2.942, 1424235599000 => 1.115}
}

or a hash of arrays:

{
  :"Apple" => [[1426049999000, 2.7235], [1424235599000, 1.124]],
  :"Orange" => [[1426049999000, 2.942], [1424235599000, 1.115]]
}

How can I create a nested hash or a hash of arrays that I want?

I tried:

my_array.group_by { |s| s[1] }

Output received:

{
  :"Apple" => [[1426049999000, "Apple", 2.7235], [1424235599000, "Apple", 1.124]],
  :"Orange" => [[1426049999000, "Orange", 2.942], [1424235599000, "Orange", 1.115]]
}

Upvotes: 0

Views: 515

Answers (4)

hirolau
hirolau

Reputation: 13901

p my_array.group_by{|x| x.delete_at(1)} #=> {"Apple"=>[[1426049999000, 2.7235], [1424235599000, 1.124]], "Orange"=>[[1426049999000, 2.942], [1424235599000, 1.115]]}


# or my_array.map(&:rotate).group_by(&:shift)

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110675

My friend @Arup has pointed out that my latter (original) expression did not return the result requested by the OP. That is true, but it would have been more accurate to say that, since I offered two ways of returning the same result (a hash whose values were arrays of hashes), neither expression returned the requested result.

In any event, I modified my original answer to conform to the OP's instructions, and wish to thank Arup for his eagle-eye.

my_array = [[1426, "Apple",  2.723],
            [1426, "Orange", 2.942],
            [1424, "Apple",  1.124],
            [1424, "Orange", 1.115]]

Expression #1

To produce a hash with values that are hashes:

my_array.each_with_object({}) { |(v1,fruit,v2),h|
  h.update(fruit.to_sym => {v1=>v2}) { |_,ov,nv| ov.update(nv) } }
  #=> {:Apple=>{1426=>2.723,  1424=>1.124},
  #    :Orange=>{1426=>2.942, 1424=>1.115}}

or with values that are arrays of arrays:

my_array.each_with_object({}) { |(v1,fruit,v2),h|
  h.update(fruit.to_sym => [[v1, v2]]) { |_,ov,nv| ov+nv } }
  #=> {:Apple=> [[1426, 2.723], [1424, 1.124]],
   #   :Orange=>[[1426, 2.942], [1424, 1.115]]} 

I would suggest the latter, in the event that [1424, "Apple", 1.124] in my_array were instead, say, [1426, "Apple", 1.124], there would not be a problem of 1426 being a duplicate key.

Expression #2

my_array.each_with_object(Hash.new {|h,k| h[k]=[]}) { |(v1,fruit,v2),h|
  h[fruit.to_sym] << [v1,v2] }
  #=> {:Apple=> [[1426, 2.723], [1424, 1.124]],
  #    :Orange=>[[1426, 2.942], [1424, 1.115]]} 

Upvotes: 1

steenslag
steenslag

Reputation: 80065

groups =  { :"Apple" => [[1426049999000, "Apple", 2.7235], [1424235599000, "Apple", 1.124]],
      :"Orange" => [[1426049999000, "Orange", 2.942], [1424235599000, "Orange", 1.115]]}

groups.each{|k,v| v.each{|ar| ar.delete(k.to_s)}}
p groups # => {:Apple=>[[1426049999000, 2.7235], [1424235599000, 1.124]], :Orange=>[[1426049999000, 2.942], [1424235599000, 1.115]]}

Upvotes: 0

Arup Rakshit
Arup Rakshit

Reputation: 118271

I'd do :

my_array = [[1426049999000, "Apple", 2.7235], [1426049999000, "Orange", 2.942], [1424235599000, "Apple", 1.124], [1424235599000, "Orange", 1.115]]
my_array.each_with_object({}) { |a, h| (h[a[1]] ||= []) << a.values_at(0, 2) }
# => {"Apple"=>[[1426049999000, 2.7235], [1424235599000, 1.124]], "Orange"=>[[1426049999000, 2.942], [1424235599000, 1.115]]}
my_array.each_with_object({}) { |a, h| (h[a[1]] ||= {}).update(a[0] => a[2]) }
# => {"Apple"=>{1426049999000=>2.7235, 1424235599000=>1.124}, "Orange"=>{1426049999000=>2.942, 1424235599000=>1.115}}

Upvotes: 2

Related Questions