Blocks and Iterators in Ruby

· 4 min read · Updated March 7, 2026 · beginner
blocks iterators yield ruby-fundamentals

Ruby blocks and iterators are fundamental to writing idiomatic Ruby code. They’re what make Ruby feel natural and expressive. In this tutorial, you’ll learn what blocks are, how to use them with iterators, and how to create your own methods that accept blocks using yield.

What Are Blocks?

A block is a chunk of code you can pass to a method. It’s not an object itself, but it can be associated with a method call. Blocks are the foundation of Ruby’s iterator methods.

# A block attached to the `each` method
[1, 2, 3].each do |number|
  puts "Number: #{number}"
end

Blocks can be written with do...end or with curly braces:

# Both are equivalent
[1, 2, 3].each { |number| puts "Number: #{number}" }

The do...end syntax is preferred for multi-line blocks, while curly braces work well for single-line blocks.

Using Built-in Iterators

Ruby provides many iterator methods on arrays, hashes, and other enumerables. These are what make Ruby so expressive.

The each Method

The most fundamental iterator is each. It yields each element to your block:

fruits = ["apple", "banana", "cherry"]

fruits.each do |fruit|
  puts "I love #{fruit}"
end
# Output:
# I love apple
# I love banana
# I love cherry

For hashes, each yields key-value pairs:

person = { name: "Alice", age: 30, city: "London" }

person.each do |key, value|
  puts "#{key}: #{value}"
end

The map Method

map transforms each element and returns a new array:

numbers = [1, 2, 3, 4, 5]

squared = numbers.map { |n| n ** 2 }
puts squared  # => [1, 4, 9, 16, 25]

This is one of Ruby’s most powerful methods for data transformation.

The select and reject Methods

Filter elements based on a condition:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

evens = numbers.select { |n| n.even? }
puts evens  # => [2, 4, 6, 8, 10]

odds = numbers.reject { |n| n.even? }
puts odds  # => [1, 3, 5, 7, 9]

The reduce Method

reduce combines all elements into a single value:

numbers = [1, 2, 3, 4, 5]

sum = numbers.reduce(0) { |acc, n| acc + n }
puts sum  # => 15

# Or using the shorthand :+ symbol
sum = numbers.reduce(:+)
puts sum  # => 15

Creating Methods with Blocks Using yield

You can create your own methods that accept blocks using the yield keyword:

def greet
  puts "Before yield"
  yield
  puts "After yield"
end

greet { puts "Inside the block!" }

# Output:
# Before yield
# Inside the block!
# After yield

Passing Data to Blocks

You can pass data to the block using arguments after yield:

def double_numbers(numbers)
  numbers.map { |n| yield n }
end

result = double_numbers([1, 2, 3, 4]) { |n| n * 2 }
puts result  # => [2, 4, 6, 8]

Checking if a Block Was Given

Use block_given? to check whether a block was provided:

def maybe_increment(number)
  if block_given?
    yield(number)
  else
    number + 1
  end
end

puts maybe_increment(5)              # => 6
puts maybe_increment(5) { |n| n * 3 } # => 15

Common Iterator Patterns

Chaining Methods

Ruby’s iterators can be chained for powerful data processing:

result = (1..20)
  .select(&:odd?)
  .map { |n| n ** 2 }
  .first(5)

puts result  # => [1, 9, 25, 49, 81]

Iterating with Index

Use each_with_index when you need the index:

fruits = ["apple", "banana", "cherry"]

fruits.each_with_index do |fruit, index|
  puts "#{index + 1}. #{fruit}"
end
# Output:
# 1. apple
# 2. banana
# 3. cherry

Range Iterators

Ruby ranges support iteration directly:

# Inclusive range
(1..5).each { |n| print "#{n} " }
# Output: 1 2 3 4 5

# Exclusive range
(1...5).each { |n| print "#{n} " }
# Output: 1 2 3 4

When to Use Blocks

Blocks are perfect for:

  • Callbacks: Run code after or around a method’s main action
  • Data transformation: Map arrays to new values
  • Filtering: Select or reject elements based on conditions
  • Accumulation: Reduce collections to single values
  • Resource management: Ensure cleanup happens (like file handling)

Summary

Blocks and iterators are what make Ruby feel like Ruby. You now understand:

  • What blocks are and how to write them
  • Common built-in iterators: each, map, select, reject, reduce
  • How to create your own methods with yield
  • How to check for blocks with block_given?
  • How to chain iterator methods for expressive data processing

Practice using these methods with your own data. The more you use them, the more natural they’ll feel.