Enumerators for brevity
I’ve recently started to reach for Ruby’s Enumerator
class more often and thought it was worth a quick mention. The functionality I’m focusing on here has been around since Ruby 1.8, but I rarely see it used in the wild.
Enumerator#each_with_index
allows us to iterate through a collection of objects, with access to the index of each object.
numbers = [1,2,3,4,5]
numbers.each_with_index do |number, idx|
puts "#{number} at index #{idx}"
end
# 1 at index 0
# 2 at index 1
# etc.
Passing the index into the block like that can be really useful, but #each_with_index
is on its own; other common Enumerable
methods are not afforded this kind of special extended treatment. Thankfully though, some of these methods return an Enumerator
when they are used without a block.
numbers.map # => #<Enumerator: [1, 2, 3, 4, 5]:map>
This means we can chain Enumerator
methods like #with_index
.
numbers.map.with_index
# => #<Enumerator: #<Enumerator: [1, 2, 3, 4, 5]:map>:with_index>
It’s especially useful in places where you might otherwise use a placeholder array.
cities = ['New York City', 'Oslo']
countries = ['USA', 'Norway']
places_1 = []
cities.each_with_index do |city, idx|
places_1 << "#{city}, #{countries[idx]}"
end
# places_1
# => ['New York City, USA', 'Oslo, Norway']
places_2 = cities.map.with_index do |city, idx|
"#{city}, #{countries[idx]}"
end
# places_2
# => ['New York City, USA', 'Oslo, Norway']
Check out just how many Enumerable
methods return an Enumerator
by searching this page for “an_enumerator”.
There’s more reading on this topic, including a decent explanation of the external iteration features introduced in 1.9, over at Wikibooks.