views:

1059

answers:

3

I seem to run into this very often. I need to build a Hash from an array using an attribute of each object in the array as the key.

Lets say I need a hash of example uses ActiveRecord objecs keyed by their ids Common way:

ary = [collection of ActiveRecord objects]
hash = ary.inject({}) {|hash, obj| hash[obj.id] = obj }

Another Way:

ary = [collection of ActiveRecord objects]
hash = Hash[*(ary.map {|obj| [obj.id, obj]}).flatten]

Dream Way: I could and might create this myself, but is there anything in Ruby or Rails that will this?

ary = [collection of ActiveRecord objects]
hash = ary.to_hash &:id
#or at least
hash = ary.to_hash {|obj| obj.id}
+4  A: 

You can add to_hash to Array yourself.

class Array
  def to_hash(&block)
    Hash[*self.map {|e| [block.call(e), e] }.flatten]
  end
end

ary = [collection of ActiveRecord objects]
ary.to_hash do |element|
  element.id
end
ewalshe
+11  A: 

There is already a method in ActiveSupport that does this.

['an array', 'of active record', 'objects'].index_by(&:id)

And just for the record, here's the implementation:

def index_by
  inject({}) do |accum, elem|
    accum[yield(elem)] = elem
    accum
  end
end

Which could have been refactored into (if you're desperate for one-liners):

def index_by
  inject({}) {|hash, elem| hash.merge!(yield(elem) => elem) }
end
August Lilleaas
I think if you change the merge to merge! you'll avoid creating a bunch of intermediate hashes you don't need.
Scott
If you are going to use this many times in the critical path of you app, you might want to consider using ary.index_by{|o| o.id} instead of using symbol_to_proc.
krusty.ar
hooray for Rails
Daniel Beardsley
@Scott: Very good point! Fixed.
August Lilleaas
A: 

Install the Ruby Facets Gem and use their Array.to_h.

Lolindrath