1
\$\begingroup\$

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?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Oct 9, 2014 at 13:41
\$\endgroup\$
0

1 Answer 1

4
\$\begingroup\$

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)
answered Oct 9, 2014 at 13:45
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.