tags:

views:

279

answers:

4

I have an array of Products, each of which has a name and a category. I would like to produce a hash in which each key is a category string and each element is a product with that category, akin to the following:

{ "Apple" => [ <Golden Delicious>, <Granny Smith> ], ...
  "Banana" => ...

Is this possible?

+1  A: 
h = Hash.new {|h, k| h[k] = []}
products.each {|p| h[p.category] << p}
Martin DeMello
+4  A: 

In 1.8.7+ or with active_support (or facets, I think), you can use group_by:

products.group_by {|prod| prod.category}
sepp2k
A: 
# a for all
# p for product
new_array = products.inject({}) {|a,p| a[p.category.name] ||= []; a[p.category.name] << p}
Eimantas
You're missing ";a" from the end of your inject block -- the block has to return the memo hash.
glenn jackman
thanks for the note! but doesn't last expression return the hash?
Eimantas
No, it returns the a[p.category.name] array (the result of the Array#<< method)
glenn jackman
A slightly shorter version using inject: products.inject(Hash.new([])) {a[p.category.name] += [p]; a}
glenn jackman
A: 

The oneliner

arr = [["apple", "granny"],["apple", "smith"], ["banana", "chiq"]]
h = arr.inject(Hash.new {|h,k| h[k]=[]}) {|ha,(cat,name)| ha[cat] << name; ha}

:-)

But I agree, #group_by is much more elegant.

Robert Klemme