# Select and Map Are Good

4 min read

This article argues that when able to one should break down iteration operations over an array into #map and #select as opposed to operating on the enumerable through an #each.

## The Examples

Throughout this article I will refer to the following, contrived, example:

You have an array of numbers [1, 2, 3, 4, 5] and you want to subtract 3 from each of the items and then remove all items that are 0.

A) Using #each you could express this as:

the_array = [1, 2, 3, 4, 5]
new_array = []

the_array.each do |item|
new_item = item - 3
if new_item != 0
new_array << new_item
end
end


B) Using #select and #map you could express this as:

the_array = [1, 2, 3, 4, 5]

new_array = the_array.map { |item| item - 3 }

new_array.select! { |item| item != 0 }


## The Arguments

### Better Seperation of Logic

Example A is doing two things in one block whereas example B is doing just one thing in each of the two blocks. In general, the less there is in a block the easier that block is to understand. Breaking a problem down into map and select means that you have broken the problem up into two distinct parts.

### Clarity

Which brings us to what those two parts do. They are actually named. If I am reading example B and I am trying to find the part where items are removed, then I look in the select block. If I am looking for the part where the items are changed then I look in the map block. The method names tell how the block is to be used.

When reading example A, I have to read all of the #each block if I am looking for where the values are changed or when the items are removed.

## Potential Counter Arguments

I think some people may argue that speed is a big issue. The idea is that you are iterating over the enumerable twice so it is using more time.

Okay let us assume the time to to process item - 3 and assign it to variable/add it to the array is a and that time to check the new_item/item != 0 and add it the array is b, and the time to setup each iteration of the array is c. We will also assume i is the number of iterations to travese the array.

So example A will take i(a + b + c) time and example B will take i(a + c) + i(b + c) . The difference between these two ends up being B - A so i(a + b) + 2ic - (i(a + b) + ic) = ic. The difference is going to be the time to setup the iterations.

Let us check the actual difference in time with a much bigger array:

require 'benchmark'

def with_each(the_array)
new_array = []

the_array.each do |item|
new_item = item - 3
if new_item != 0
new_array << new_item
end
end

new_array
end

def with_map_and_select(the_array)
new_array = the_array.map { |item| item - 3 }

new_array.select! { |item| item != 0 }
end

the_array = (-10000000..10000000).to_a

Benchmark.bmbm do |x|
x.report("With #each") { with_each(the_array) }
x.report("With #map and #select") { with_map_and_select(the_array) }
end


The output on my computer is:

                            user     system      total        real
With #each              1.830000   0.040000   1.870000 (  1.924606)
With #map and #select   2.440000   0.030000   2.470000 (  2.468480)


So the time difference is about +25% for example B and this is a really simple example. The difference as a percentage will fall as the complexity of the operations increases.

So the time difference does exists but I don't think this is going to be a huge factor in most cases.