Understanding Ruby `each_with_object` method

It is always nice to do the same thing in a better and quicker way. A problem that occurs quite often in my everyday work is converting a hash to an array of hashes with a different structure.

The problem

Let’s assume that we have a following hash:

hash = {
  name: 'John',
  surname: 'Smith',
  age: '25',
  city: 'Gdańsk'
}

and for a some reason, like external API integration, we need an array of hashes looking like this:

array = [
  { name: 'name', content: 'John' },
  { name: 'surname', content: 'Smith' },
  { name: 'age', content: '25' },
  { name: 'city' content: 'Gdańsk' }
]

My previous approach

I used to create a method with an empty array defined and push custom hashes into the array using each iterator. Also the method needs to return the array.

def hash_to_array_of_hashes
  array = []

  hash.each do |key, value|
    array << { name: key.to_s, value: value }
  end

  array
end

Better solution

each_with_object is a less popular method that belongs to Enumerable mixin. According to the Ruby 2.2.2 documentation:

Iterates the given block for each element with an arbitrary object given, and returns the initially given object.

each_with_object(obj) { |(*args), memo_obj| ... } → obj

The most tricky part for me was this memo object. Basically you can provide any kind of object and it will be returned. Let’s consider a simple example and create an array of numbers from 1 to 10.

(1..10).each_with_object([]) { |number, memo_array| memo_array << number }

=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

I also needed an array, but of hashes so I improved the above code:

hash.each_with_object([]) do |(key, value), memo_array|
  memo_array << { name: key.to_s, content: value }
end

=> [{:name=>"name", :content=>"John"}, {:name=>"surname", :content=>"Smith"}, {:name=>"age", :content=>"25"}, {:name=>"city", :content=>"Gdańsk"}]

and it just did the job.

Summary

It is never late to learn. I enjoy doing things better, in a more clever way.

Have you known each_with_object method or maybe you know a better solution for the above problem?

 

Igor Springer

I build web apps. From time to time I put my thoughts on paper. I hope that some of them will be valuable for you. To teach is to learn twice.