Ruby Iterator — Each

Tony Lum
3 min readApr 26, 2019

--

Mox Ruby | Art by Volkan Baga, also a fan of MtG and puns…

When I first started learning Ruby, one of the most helpful things I did was learning to breakdown methods that come packaged with a language. It helped me understand what the method was actually doing as well as how to create my own version of the method if I ever needed something similar but not the exact same thing.

I wanted to go over one of the first Ruby array methods that most programmers will come across, each. Each can be called on arrays or hashes. When each is called on the datasets, it will always return the original dataset without any mutation. This means that no matter what happens inside of the iteration, even if you somehow mutate the original dataset, the return value will still be the original array sent in.

array = [1,2,3,4,5]
array.each { |element| element + 1 }
#[1,2,3,4,5]
array
#[1,2,3,4,5]

In the snippet above, when each is called on the array, it goes through each element of the array and “does something” to it, in this case, adds 1 to it. Note that the original array is not modified and the output is the value that entered the array.

So why is this useful? It helps to understand how other iterators in ruby works, and serve as a baseline to methods such as map, all?, none?, reject, select, each_with_index, etc.

Under the hood, each is written as similar to:

def each(dataset)
index = 0
while index < dataset.length
yield(dataset[i])
index += 1
end
dataset
end
-- or --Class Array
def each
index = 0
while index < self.length
yeild(self[index]
index += 1
end
self
end
end

From ruby docs:

VALUE
rb_ary_each(VALUE ary)
{
long i;

RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_AREF(ary, i));
}
return ary;
}

At the end of the method, the dataset(or self, if you are following the second example) that is passed in will be returned back to the user. This helps to explain why if a user manipulates the array using each, the original value is returned, but the original dataset is overwritten.

array = [1,2,3,4,5]p array.each { |element| array = "some modification"} 
#[1,2,3,4,5], note that array does not equal [1,2,3,4,5] but return statment will be.
p array
#"some modification"

While this is highly impractical, it can be done. At the end of the day, the array is just a variable. It starts off pointing at [1,2,3,4,5] in memory. At the iterator, we are resetting the array variable to “some modification”, and note we do this multiple times. At the end of the iteration, the original array is still returned because that is what the each method was programmed to do.

I hope to be diving a bit deeper into other methods across different languages and find out the small nuances that make them tick or compute. Most of the time it might be ruby since it has a special place in my heart for it.

--

--