Ruby Enumerators

Powerful iteration and collection processing in Ruby

🔄 What are Enumerators?

Enumerators are objects that allow you to iterate over collections in a controlled way. They provide lazy evaluation and chainable methods for efficient data processing without loading everything into memory.


# Basic enumerator
enum = [1, 2, 3].each
puts enum.next
puts enum.next
                                    

Output:

1

2

Key Enumerator Concepts

➡️

Next Method

Get the next element

enum.next
⏮️

Rewind

Reset to the beginning

enum.rewind
🔗

Chaining

Combine multiple operations

enum.map.with_index
💤

Lazy Evaluation

Process data on demand

(1..Float::INFINITY).lazy

🔹 Creating Enumerators

You can create enumerators from arrays, ranges, or any enumerable object. Enumerators give you fine control over iteration, allowing you to pause and resume as needed.

# Create enumerator from array
numbers = [10, 20, 30, 40]
enum = numbers.each

puts enum.next  # 10
puts enum.next  # 20
enum.rewind
puts enum.next  # 10 (back to start)

Output:

10

20

10

🔹 Enumerator with Index

Track both the element and its position during iteration. The with_index method adds an index counter to your enumeration, perfect for numbered lists or position-aware processing.

# Enumerate with index
fruits = ["apple", "banana", "cherry"]

fruits.each.with_index do |fruit, index|
  puts "#{index + 1}. #{fruit}"
end

Output:

1. apple

2. banana

3. cherry

🔹 Lazy Enumerators

Lazy enumerators process elements only when needed, making them perfect for large or infinite sequences. They don't compute all values upfront, saving memory and improving performance for big datasets.

# Lazy evaluation for efficiency
numbers = (1..Float::INFINITY).lazy
  .select { |n| n % 2 == 0 }
  .map { |n| n * 2 }
  .first(5)

puts numbers.inspect

Output:

[4, 8, 12, 16, 20]

🔹 Custom Enumerators

Build your own enumerators using Enumerator.new with a block. This lets you define custom iteration logic for any data source or pattern you need.

# Create custom enumerator
fibonacci = Enumerator.new do |yielder|
  a, b = 0, 1
  loop do
    yielder << a
    a, b = b, a + b
  end
end

puts fibonacci.first(7).inspect

Output:

[0, 1, 1, 2, 3, 5, 8]

🔹 Enumerator Chaining

Combine multiple enumerator methods to create powerful data processing pipelines. Chaining allows you to filter, transform, and manipulate data in a clean, readable way.

# Chain multiple operations
result = [1, 2, 3, 4, 5, 6]
  .each
  .select { |n| n.even? }
  .map { |n| n ** 2 }
  .to_a

puts result.inspect

Output:

[4, 16, 36]

🔹 Practical Enumerator Example

Process a large file line by line without loading it all into memory. This demonstrates how enumerators excel at handling large datasets efficiently by processing one item at a time.

# Simulate reading large file
lines = ["Line 1", "Line 2", "Line 3", "Line 4", "Line 5"]
enum = lines.each

# Process one line at a time
3.times do
  begin
    line = enum.next
    puts "Processing: #{line}"
  rescue StopIteration
    puts "No more lines"
    break
  end
end

Output:

Processing: Line 1

Processing: Line 2

Processing: Line 3

🧠 Test Your Knowledge

What method resets an enumerator to the beginning?