Array to a hash, ruby code reduction I have an array of thing objects, each of which has an array of categories. I'd like to build a category hash with a category ID for a key and an array of things for a value.

category_hash = Hash.new

things.each { |thing|
  thing.categories.each { |category|
    category_hash[category] = Array.new unless category_hash[category]
    category_hash[category] &lt;&lt; thing
  }
}


Purely for education, how can this be shortened?
There are several ways of making this more idiomatic:


A trivial note, but don't use Array.new.

Use [], it's shorter, clearer, and more idiomatic
Don't use category_hash[category] = [] unless category_hash[category]

Instead, use category_hash[category] ||= []. In general you can use a ||= b instead of a = b unless a. In the case of hashes, you can skip this completely and just give the hash an appropriate default value:

category_hash = Hash.new { |hash,key| hash[key] = [] }

Don't initialize a collection to an empty state, and then iterate over another collection, appending to the new collection. Use map/each_with_object/inject/etc. to turn one collection into another collection


A more idiomatic solution might look like this:

category_hash = things.each_with_object(Hash.new {|h,k| h[k] = [] }) do |thing,hash|
  thing.categories.each { |cat| hash[cat] &lt;&lt; thing }
end


A completely different approach would be to map each thing's categories into a new hash, and then merge the resulting hashes. The problem is that you need a "deep" merge, like the one that comes with ActiveSupport:

categories_hash = things.map do |thing|
  Hash[thing.categories.map { |cat| [cat,thing] }]
end.reduce(&amp;:deep_merge)