Reason #6 • January 6th, 2026

Enumerator

Building on yesterday's reason about Enumerable, today I'd like to take a quick look at Enumerator.

Whenever you call an Enumerable method without a block, it returns an Enumerator object instead of executing the method immediately. This allows for lazy evaluation and chaining of operations (though we'll only focus on chaining for today).

Ruby
languages = %w[Ruby JavaScript TypeScript]
ranked_languages = languages
  .map.with_index(1) { |language, rank| "#{rank}. #{language}" }
# => ["1. Ruby", "2. JavaScript", "3. TypeScript"]

ids = 1..23
batches =
  ids
    .each_slice(5) # yields slices of 5 elements each
    .with_index(1) # passing 1 starts index at 1 instead of 0
    .map { |ids, batch_number| { batch_number:, ids: } }
# => [{ batch_number: 1, ids: [1, 2, 3, 4, 5] }, ...]
      
JavaScript
const languages = ['Ruby', 'JavaScript', 'TypeScript'];
const rankedLanguages = languages
  .map((language, index) => `${index + 1}. ${language}`);
// => ['1. Ruby', '2. JavaScript', '3. TypeScript']

const ids = Array.from({ length: 23 }, (_, i) => i + 1);
const batches = [];
for (let i = 0; i < ids.length; i += 5) {
  batches.push({
    batchNumber: Math.floor(i / 5) + 1,
    ids: ids.slice(i, i + 5),
  });
}
// [{ batchNumber: 1, ids: [1, 2, 3, 4, 5] }, ...]
      

Note how in the Ruby example we chain methods like map, each_slice and with_index seamlessly, which keeps the code concise and expressive, virtually free of "computer language".

Using Enumerator directly

You can also create your own Enumerator objects using the Enumerator.new method. Here's how to create a Fibonacci sequence generator. Feel free to compare with yesterday's example using Enumerable and think about when you'd choose one approach over the other.

Ruby
fibonacci = Enumerator.new do |yielder|
  a, b = 0, 1
  loop do
    yielder << a
    a, b = b, a + b
  end
end
fibonacci.take(10)
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
    

History

The Enumerator class was introduced in Ruby 1.8 (released in 2003) to provide a way to create external iterators. However, Enumerable methods didn't start returning Enumerator objects when no block was given until 1.8.7 (released in 2008), and the behavior wasn't fully standardized until Ruby 1.9. My personal journey with Ruby began in 1.8.5, so I remember the excitement as these improvements rolled out!