Who Can Win? ‘Map’, ‘Inject’ or ‘Eachs’?

Yunus Emre AYBEY
5 min readAug 19, 2019

--

Image by Pixabay on Pexels

“Speed is inconsequential if you are headed in the wrong direction.”
Matshona Dhliwayo

In this article you will learn about:

  • Ruby methods which are each, each_with_object, inject and map.
  • Which one is faster
  • When and Why you should use them.

A person can say Ruby is a programming language which gives the impression of a normal spoken language.

Ruby is an amazing programming language which is popular with its simplicity and productivity. A person can say it is a programming language which gives the impression of a normal spoken language. It has plenty of methods which make coding easier. If you wanna learn more about it you can click here. In Ruby, there is a term called ‘Enumerable’ which is also a module in ruby. We can call an object enumerable when it describes a set of items and a method to loop over each item. Today I am going to talk about four enumerable methods. Let’s start with the famous each loop of Ruby.

EACH

Each method is a simple way of looping through an array, range or hash. You can reach every element of that enumerable object and do something with them. Here is an example:

[1, 2, 3].each {|num| puts num }
1
2
3
=> [1, 2, 3]

As you see it iterates through the array prints the items and then returns the array itself.

EACH WITH OBJECT

The syntax is nearly the same for each for each_with_object method. The only difference is, it is taking an enumerable object as a parameter and then returns it. Here is an example:

[1, 2, 3].each_with_object(Hash.new) {|num, hash| hash[num] = “My index is #{num-1}” }
=> {1=>”My index is 0", 2=>”My index is 1", 3=>”My index is 2"}

INJECT

Inject method is a little bit different actually. Ruby apidock defines inject as ‘Combines all elements of enum by applying a binary operation, specified by a block or a symbol that names a method or operator.’ Here is the example which you can understand easily :

[1, 2, 3].inject {|sum, num| sum + num}
=> 6

you can also use this syntax :

[1, 2, 3].inject(:+)

but this article is not about syntaxes.

MAP

‘Map’ is another enumerable method. The only difference is, it alters the original array and returns it. Here is an example:

[1, 2, 3].map {|num| num * 2}
=> [2, 4, 6]

Which One Is Faster?

To test the speed of the methods we will use a gem called benchmark-ips which will test all the methods in terms of speed automatically. I will not talk about benchmark much but you can easily install it by this command in your terminal:

gem install benchmark-ips

you can see the syntax from the given examples but if you want to learn more about benchmark just click here.

Basically, we will write four methods. Each will use the aforementioned enumerable methods and do the same thing. Finally, benchmark-ips will show us the winner of the race.

Test Results

Here is our test array:

arr = Array.new(999999, 2)

It has 999999 elements and all of them are 2 by default.

Here is the code that defines and tests the methods:

require ‘benchmark/ips’
arr = Array.new(999999, 2)
def normal_each(arr)
res = []
arr.each {|num| res << (num * 2)}
res
end
def the_inject(arr)
arr.inject([]) {|acc, num| acc << (num * 2)}
end
def the_object(arr)
arr.each_with_object([]) {|num, arr| arr << (num * 2)}
end
def mapping(arr)
arr.map {|num| num * 2}
end
Benchmark.ips do |x|

x.report(“Each method”) {normal_each(arr)}
x.report(“Inject Method”) {the_inject(arr)}
x.report(“Map Method”) {mapping(arr)}
x.report(“Each With Object Method”) {the_object(arr)}
x.compare!
end

Basically, every method takes the same array and then multiplies every element with two and finally returns the new array. Here are the test results:

Test Results

What This Result Says

Here, our champion is the map method. If you read until this part, you may be thinking this article says: Okay, the map is the best, use it. Unfortunately, you are wrong! The reason the map method is faster here is that it is built for these situations. In our example, every method is trying to do the same thing, altering the original array by multiplying every element of it with 2. The map method is the right tool to do this. Now we can answer the first question of this article, who can win? ‘map’, ‘inject’ or ‘eachs’? The answer is no one. It all depends on the situation. Here is an example where the champion is ‘inject’ :

require ‘benchmark/ips’arr = [250, 730, 896, 125, 1000]def normal_each(arr)
res = 0
arr.each {|num| res += num}
res
end
def the_inject(arr)
arr.inject(:+)
end
def the_object(arr)
arr.each_with_object([0]) {|num, arr| arr[0] += num }.first
end
def mapping(arr)
res= 0
arr.map {|num| res += num}
res
end
Benchmark.ips do |x|

x.report(“Each method”) {normal_each(arr)}
x.report(“Inject Method”) {the_inject(arr)}
x.report(“Map Method”) {mapping(arr)}
x.report(“Each With Object Method”) {the_object(arr)}
x.compare!
end

The result is :

Test Results

Assume that you have an array which has the number of people in all departments. Every index is representing the department number and the items are representing the people in that department. You would like to see the total number of people in the company. Our example is representing this example and as you see the inject is the winner. To sum up, every method is good at something. So, if you want to solve a problem with the fastest method, you need to understand your problem and the way that methods work. If you want to find the sum, inject is better. If you want to alter the original object with the same process for every item, the map is better. If you want to print every item in the object in a special way, each is better. If you want to return another type of enumerable object by using the original object, each_with_object is better.

CONCLUSION

In this article I tried to show you:

  • Ruby methods which are each, each_with_object, inject and map.
  • Being fast depends on the situation.
  • If you understand the problem and how methods work, you will easily come up with a solution to when and why you should use a specific method.

Ruby has many built-in methods which are there to do some important job. We can't just say a method is better than the other one. It all depends on the situation you are in. I would like to end my article with the same quote above:

“Speed is inconsequential if you are headed in the wrong direction.”
Matshona Dhliwayo

Special thanks to Microverse for encouraging me to write an article, team-10-sharks (My study team in Microverse), Neer Thapa and Grace Mugoiri for helping me to write this article.

--

--