I recently needed to create a hash lookup from an object using the ID as the lookup key and the full object as the value in our rails app. It wasn’t a difficult concept and there were several ways this could be accomplished; however, we just couldn’t find a way that made sense or didn’t involve looping. That is, until we found index_by.


Let’s assume we have an Animal class and an Animals list class:

class Animal
  def initialize(id, name)
    @id = id
    @name = name
  attr_reader :id, :name

class Animals
  def initialize(animals)
    @animals = animals

  def animal_list
    animals.each_with_index.map { |animal, i| Animal.new(i, animal) }
  attr_reader :animals

So we’d have a list of animals:

animal_array = ['King Mukla', 'Misha', 'Leokk', 'Huffer']
animals = Animals.new(animal_array).animal_list
=> [
 [0] #<Animal:0x007fde9678e098 @id=0, @name="King Mukla">,
 [1] #<Animal:0x007fde9678e070 @id=1, @name="Misha">,
 [2] #<Animal:0x007fde9678e048 @id=2, @name="Leokk">,
 [3] #<Animal:0x007fde9678e020 @id=3, @name="Huffer">

Using Hash[a]

Our first solution took advantage of Hash:

hash = animals.map { |animal| [animal.id, animal] }

This was our default approach in conversion in the past, but creating an array of arrays just to use this approach seemed lengthy and unnecessary.

Using Enumerable .reduce()

Our second solution took advantage of the reduce method in the Enumerable Class:

animals.reduce({}) { |hash, animal|
  hash[animal.id] = animal

This is slightly better, since we don’t have to map on the array only to convert; but needing to return the hash at the end isn’t ideal either. Both solutions so far would usually require a doc lookup for .reduce and Hash[a].

Using Enumerable .index_by()

Our final solution, we used the Enumerable index_by:


Note: .index_by accepts a block. So if you want to index by two fields such as first and last name, you could easily do this with the block:

people.index_by { |person| "#{person.first_name} #{person.last_name}" }

All three solutions yield the same result; a hash with the keys being the ID of the object:

  0 => #<Animal:0x007fde935ce530 @id=0, @name="King Mukla">,
  1 => #<Animal:0x007fde935ce440 @id=1, @name="Misha">,
  2 => #<Animal:0x007fde935ce418 @id=2, @name="Leokk">,
  3 => #<Animal:0x007fde935ce3f0 @id=3, @name="Huffer">