When it comes to doing the same thing over and over again, Ruby has a few methods you can choose from. But in this article, we’re going to look at the each method, how to use it, and what you can do with it.
When you think about Ruby, you think OOP. And that’s ok. Ruby is an Object-Oriented Programming language after all.
But, Ruby being a beautiful beast of a programming language, it also lets you combine OOP with functional programming. Don’t worry; you’ve probably done this without realizing it.
Functional programming lets you describe what you want to do, as opposed to how you want it done. An example of what would be “Give me all the even numbers in this list.“. You don’t really care about how it’s doing it, you’re telling it what you want.
[1,2,3].select(&:even?)
Forget about the syntax and how select
works right now, and just think about how expressive that operation is.
How does each work in Ruby?
each
is just another method on an object. That means that if you want to iterate over an array with each
, you’re calling the each
method on that array object.
It takes a list as it’s first argument and a block as the second argument. For every element in the list, it runs the block passing it the current element as a parameter.
You should use each
when you want iteration but don’t care about what it returns.
But let’s see an example.
Looping through an array with each
[1, 2, 3].each { |n| puts "Current number is: #{n}" }
Current number is: 1
Current number is: 2
Current number is: 3
For every element in the array, each
runs the block, passing it the element as an argument (n in the example above).
You don’t have to pass the block inline. A multiline block is recommended when you’ve got multiple lines in the block, or when the line is too long. It improves readability.
[1, 2, 3].each do |n|
text = "Current number is: #{n}"
puts text
end
Current number is: 1
Current number is: 2
Current number is: 3
Looping through a hash with each
It might sound surprising that you can actually iterate over a hash, but if you think about it, a hash is a list of key value pairs.
And the fact that the each
method is just a method, on a hash object in this case, you can implement it to behave just like it does with an array object.
my_hash = {min: 2, max: 5}
my_hash.each { |key, value| puts "k: #{key}, v: #{value}" }
k: min, v: 2
k: max, v: 5
The difference is that in this example, the block receives two arguments. The first argument is a key, and the second one is the value.
each vs. each_pair when looping through a hash
Ruby calls an object that can be iterated over, an enumerable. And it provides an Enumerable
module that you can use to make an object an enumerable.
There are a few methods you need to implement to become an enumerable, and one of those is the each
method.
So because Hash
is also an enumerable, it needs to implement the each
method. But it also has a better named one, called each_pair
.
Basically, they are just aliases for each other. They share the same code.
How do you set a starting offset
It’s sometimes useful to not start at the beginning of the list, but to skip a few items. So to set a different starting point for the loop, you can use a range.
my_list = [1, 2, 3, 4 ,5]
[3..-1].each { |i| puts i }
my_list[3..-1].each { |i| puts i }
4
5
Another way is to use drop
. It’s part of the Enumerable
module, so every enumerable object supports it. As the name suggests, drop
(n) skips the first n elements and uses the rest for the loop.
[1, 2, 3, 4 ,5].drop(3).each { |i| puts i }
4
5
How to break out from the each block
There are cases in which you don’t want to continue running the loop if some condition has been met. In that case you can use the break keyword.
[1,2,3].each do |i|
break if i == 3
puts i
end
1
2
In this example, we check to see if the value of i is equal to 3. And if it is, we call break
which stops everything and gets out of the loop.
For vs. each in Ruby
Those coming from an imperative language might be more familiar with the for
loop. Let’s see how a for loop looks like.
# imperative for
for i in [1,2,3] do
puts i
end
# functional each
[1,2,3].each { |i| puts i }
As you can imagine, the results are identical. So why would you then choose one over the other?
There is a scope difference. After the for
loop runs, the iterator variable (i.e. i) still exists. That doesn’t happen with each
, because each
introduces a new lexical scope.
for i in [1,2,3] do
puts i
end
puts i # => 3
[1,2,3].each { |n| puts n }
puts n # => NameError: undefined local variable or method `n'
And if you really need the iterator value where the loop exited, you can do it like this.
last_value = [1, 2, 3].each do |n|
break n if n % 2 == 0
end
You should use each
over for
. It is more idiomatic, and faster than for.
Finding out current index in an each loop
It’s sometimes useful to know where you are in the list, so for that you need to have an index. While each
doesn’t give you that, you can use each_with_index
.
It takes two parameters. The first one is the element, and the second one is the index.
['a', 'b', 'c'].each_with_index { |el, i| puts i }
0
1
2
Is it possible to access the index in a Hash each loop?
You’ve seen how all enumerators have the same methods and such. So if Hash
is an Enumerator
, like Array
is, then it should be possible to have that index on a hash too. Right? Right!
[1, 2, 3].each_with_index { |(k, v), i| puts i }
0
1
2
There you go. You now know how to use the each
method.