The each Method

Published by Programming Line in Ruby. Last updated .

The each method is the most fundamental way to iterate over a collection in Ruby. Instead of writing a manual counter loop like you might in other languages, you hand each a block of code, and it runs that block once for every element in the collection. Nearly every other iteration method in Ruby — map, select, reduce, and more — is built on the same idea, so understanding each deeply is the foundation for understanding all of Ruby’s Enumerable module.

Overview: How each Works

each is defined on Array, Hash, Range, and any class that includes the Enumerable module and implements its own each. Conceptually, each does one thing: it walks through the collection from the first element to the last, and for every element it yields that element to the block you provided. “Yielding” means Ruby temporarily jumps into your block, hands it the current element as an argument, runs the block’s code, and then jumps back into each to continue with the next element.

A crucial fact that trips up many newcomers: each always returns the original collection it was called on, not the result of the block. The block’s return value is computed and then thrown away. This makes sense once you think about what each is for — it exists purely for its side effects (printing, logging, appending to some other collection, updating a database, etc.), not for producing a new collection. If you want a transformed collection, you want map, not each.

Under the hood, each is typically implemented in C for the built-in classes (Array#each, Hash#each), which is why it’s so fast, but the effect is exactly as if it were written in Ruby using indices. For your own classes, you can include Enumerable and define just one method, each, and you instantly get map, select, reduce, sort, and dozens of other methods for free — they’re all implemented in terms of repeatedly calling your each.

Because a block is not itself an object in Ruby (unlike a Proc or lambda), each is the mechanism that lets you pass “a chunk of code” around without wrapping it in an object first. Ruby methods can accept exactly one implicit block, and yield (used internally by each‘s implementation) is how that block gets invoked.

Syntax

collection.each do |element|
  # code that uses element
end

collection.each { |element| puts element }
  • collection — any object that responds to each: an Array, Hash, Range, Set, or a custom Enumerable.
  • do…end — the multi-line block form; conventionally used for blocks with more than one line.
  • { } — the curly-brace block form; conventionally used for short, single-line blocks.
  • |element| — the block parameter(s). For an Array, this is one variable per element. For a Hash, Ruby automatically destructures each [key, value] pair, so you typically write |key, value|.
  • The return value of collection.each { ... } is always collection itself.

Examples

Example 1: Iterating over an Array

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

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

For each string in fruits, Ruby assigns it to the block variable fruit and runs puts "I like #{fruit}". The block runs three times, once per element, in order.

Example 2: each Returns the Original Collection

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

result = numbers.each do |n|
  n * 2
end

puts result.inspect
[1, 2, 3, 4, 5]

Even though the block computes n * 2 for every element, that value is discarded. result is set to whatever each returned, which is the original numbers array, completely unchanged. This is the single most important thing to internalize about each: it is for side effects, not for building a new value.

Example 3: Iterating a Hash and Using each_with_index

scores = { "alice" => 92, "bob" => 85, "carol" => 78 }

scores.each do |name, score|
  puts "#{name.capitalize}: #{score}"
end

["x", "y", "z"].each_with_index do |letter, index|
  puts "#{index}: #{letter}"
end
Alice: 92
Bob: 85
Carol: 78
0: x
1: y
2: z

For a Hash, each element yielded is actually a two-element array, [key, value]. Writing |name, score| instead of a single |pair| lets Ruby automatically destructure that array into two variables. Ruby’s hashes remember insertion order, so the iteration always visits "alice", "bob", then "carol", in the order they were added. each_with_index is a related Enumerable method that yields both the element and its position, useful when you need to know where you are in the collection.

How It Works Step by Step

When Ruby evaluates numbers.each { |n| ... }, roughly the following happens:

  1. Ruby locates the each method by looking up the method lookup chain starting at numbers‘s singleton class, then Array, then any modules Array includes, and so on — the same ancestor-chain lookup used for every method call in Ruby.
  2. each‘s internal implementation begins walking the array from index 0.
  3. For each element, it calls yield(element), which transfers control into your block, binding element to the block parameter n.
  4. Your block runs to completion; its final expression’s value is computed but not stored anywhere by each.
  5. Control returns to each, which advances to the next element and repeats.
  6. Once every element has been yielded, each returns the original collection.

One important wrinkle: if the block executes break, iteration stops immediately and each returns whatever value was passed to break instead of the collection.

result = [1, 2, 3, 4].each do |n|
  break "found #{n}" if n == 3
end

puts result
found 3

Here the loop reaches n == 3, executes break "found 3", and each‘s call immediately evaluates to "found 3" rather than [1, 2, 3, 4]. This is different from a plain return inside the block: since a block is not a lambda, return would exit the enclosing method entirely (or raise a LocalJumpError at the top level), whereas break only exits the each call itself.

Common Mistakes

Mistake 1: Using each When You Mean map

Because each runs a block for every element, it’s tempting to assume it also collects the block’s results into a new array. It does not.

numbers = [1, 2, 3]
doubled = numbers.each { |n| n * 2 }
puts doubled.inspect
[1, 2, 3]

doubled is just the original array, unchanged — not the doubled values. Use map when you want a new, transformed array:

numbers = [1, 2, 3]
doubled = numbers.map { |n| n * 2 }
puts doubled.inspect
[2, 4, 6]

Mistake 2: Mutating a Collection While Iterating It With each

Modifying an array’s length (adding or removing elements) while each is walking through it by index is unsafe — elements can shift out from under the iterator and get silently skipped.

items = ["a", "b", "c", "d"]

items.each do |item|
  items.delete(item) if item == "b"
end

puts items.inspect
["a", "c", "d"]

This looks like it should just remove "b", but watch closely: after "b" is deleted, the array becomes ["a", "c", "d"] and every later element shifts one position to the left. The iterator’s internal index moves on to position 2 as usual, but position 2 now holds "d" instead of "c" — so "c" is silently never visited by the block. The output happens to look correct here only because the condition didn’t need to match "c"; in general this pattern is a real bug source. The safe fix is to build a new collection instead of mutating the one you’re iterating:

items = ["a", "b", "c", "d"]
items = items.reject { |item| item == "b" }
puts items.inspect
["a", "c", "d"]

reject (and its opposite, select) return a brand-new array, leaving the original untouched while it’s being read, so nothing shifts mid-iteration.

Best Practices

  • Reach for each only when you want side effects (printing, logging, writing to a file, pushing into an external array). If you want a new transformed collection, use map; if you want a filtered subset, use select or reject; if you want to combine everything into one value, use reduce.
  • Prefer do...end for multi-line blocks and { } for short one-liners — this is the community-standard style and makes code easier to scan.
  • Never rely on the return value of each for anything other than “the original collection, or a break value.”
  • Never add or remove elements from the exact collection you’re iterating over with each. Iterate over a copy (collection.dup) or, better, build the result with a non-mutating method like select/reject/map.
  • When iterating a Hash, destructure directly in the block parameters (|key, value|) rather than accepting a single pair and indexing into it — it reads more clearly.
  • If you only need the index alongside the element, use each_with_index rather than manually tracking a counter variable.
  • Remember Ruby’s truthiness rules while writing conditions inside a block: only nil and false are falsy, so an empty array, an empty string, and 0 are all truthy and will not skip an if check.

Practice Exercises

  • Given words = ["ruby", "is", "fun"], use each to print each word in uppercase, one per line.
  • Given inventory = { "apples" => 10, "pears" => 0, "plums" => 5 }, use each to print "item is out of stock" for any item whose count is 0, and "item: count in stock" otherwise. (Hint: remember that 0 is truthy in Ruby — check with == 0, not by treating the count as a boolean.)
  • Given numbers = [3, 7, 2, 9, 4], use each together with break to find and print the first number greater than 5, then stop iterating. Expected output: 7.

Summary

  • each runs a block once per element of a collection, purely for side effects.
  • each always returns the original collection (or a break value), never the block’s results — use map if you need a transformed collection.
  • For a Hash, each yielded element is a [key, value] pair, which you can destructure directly as |key, value|.
  • each_with_index yields both the element and its position when you need to track location.
  • Never mutate the length of a collection you are actively iterating with each — elements can be silently skipped; iterate a copy or build a new collection instead.
  • break inside an each block stops iteration immediately and becomes the call’s return value; a bare return behaves differently because a block is not a lambda.