# 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] << 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] << 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(&:deep_merge)