Ruby Loops and Iteration — each while until times and Enumerable Explained
In this tutorial, you will learn about Ruby Loops and Iteration. We cover key concepts, practical examples, and best practices to help you master this topic.
Ruby loops and iteration include each for collections, while/until with conditions, times/upto for counting, and the loop keyword for infinite iteration with explicit break control.
What You'll Learn
- All Ruby loop constructs: each, while, until, for, times, upto, downto, loop
- Break, next, and redo for loop control
- The Enumerable module and its iteration methods
- When to use each type of loop
Why It Matters
Loops let you Process collections, read files, and handle repetitive tasks efficiently. In Durga Antivirus Pro, loops scan thousands of files for malware signatures. DodaZIP uses loops to compress batches of files. Doda Browser iterates over DOM elements, network requests, and cache entries. Without loops, every operation would need to be written manually for each item.
Real-World Use
A Rails background job might loop through 10,000 email recipients using each, while a data processing script uses while to read a CSV line by line until the file ends. The right loop choice makes code faster and clearer.
flowchart LR
A["Loops & Iteration"] --> B["each"]
B --> C["while/until"]
C --> D["times/upto"]
D --> E["loop + break"]
E --> F["Arrays"]
A:::current --> B
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
style F fill:#f1f5f9,stroke:#94a3b8,color:#64748b
The Each Loop
each is the most common Ruby loop. It iterates over every element in a collection:
[1, 2, 3].each do |number|
puts number * 2
end
# 2
# 4
# 6
The block variable |number| receives each element in turn. Ruby blocks can use do...end or { }:
[1, 2, 3].each { |n| puts n * 2 }
Each with Index
fruits = ["apple", "banana", "cherry"]
fruits.each_with_index do |fruit, index|
puts "#{index}: #{fruit}"
end
# 0: apple
# 1: banana
# 2: cherry
Each on Different Types
# Hash
{ name: "Alice", age: 25 }.each do |key, value|
puts "#{key}: #{value}"
end
# name: Alice
# age: 25
# Range
(1..5).each { |n| print n }
# 12345
# String lines
"line1\nline2\nline3".each_line { |line| puts line.strip }
While Loop
while runs as long as the condition is truthy:
count = 0
while count < 5
puts "Count: #{count}"
count += 1
end
# Count: 0
# Count: 1
# Count: 2
# Count: 3
# Count: 4
While Modifier
count = 0
puts count += 1 while count < 5
# 1 2 3 4 5
File Reading with While
file = File.open("data.txt", "r")
while line = file.gets
puts line.chomp
end
file.close
Until Loop
until is the opposite of while — it runs while the condition is falsy:
count = 0
until count == 5
puts "Count: #{count}"
count += 1
end
# Count: 0
# Count: 1
# Count: 2
# Count: 3
# Count: 4
Until Modifier
count = 0
puts count += 1 until count == 5
For Loop
Ruby has a for loop, but it's rarely used because each is more idiomatic:
for i in 1..5
puts i
end
# 1 2 3 4 5
# Equivalent each
(1..5).each { |i| puts i }
The difference: for doesn't create a new scope (the variable i exists after the loop), while each creates a block scope.
Times Loop
times repeats a block a specific number of times:
3.times { puts "Hello" }
# Hello
# Hello
# Hello
3.times do |i|
puts "Iteration #{i}"
end
# Iteration 0
# Iteration 1
# Iteration 2
Upto and Downto
upto counts up, downto counts down:
1.upto(5) { |i| print i }
# 12345
5.downto(1) { |i| print i }
# 54321
# With step
1.step(10, 2) { |i| print i }
# 13579
The Loop Keyword
loop creates an infinite loop that you must break explicitly:
count = 0
loop do
puts count
count += 1
break if count >= 5
end
# 0 1 2 3 4
This is useful for situations where you don't know the exit condition in advance:
# Read until end of file
loop do
line = file.gets
break if line.nil?
puts line
end
Break, Next, and Redo
Break — Exit the Loop
[1, 2, 3, 4, 5].each do |n|
break if n == 3
puts n
end
# 1
# 2
Next — Skip to Next Iteration
[1, 2, 3, 4, 5].each do |n|
next if n.even?
puts n
end
# 1
# 3
# 5
Redo — Repeat Current Iteration
attempts = 0
[1, 2, 3].each do |n|
attempts += 1
puts "Attempt #{attempts}: #{n}"
redo if attempts < 2
end
# Attempt 1: 1
# Attempt 2: 1
# Attempt 1: 2
Retry — Restart the Block (Legacy)
# In Ruby < 2.0, retry restarted the iteration
# In modern Ruby, retry only works in begin/rescue blocks
Enumerable Methods for Iteration
Ruby's Enumerable module provides sophisticated iteration beyond basic each:
numbers = [1, 2, 3, 4, 5]
# Select — filter truthy results
evens = numbers.select { |n| n.even? }
puts evens.inspect
# [2, 4]
# Reject — opposite of select
odds = numbers.reject { |n| n.even? }
puts odds.inspect
# [1, 3, 5]
# Map — transform each element
squares = numbers.map { |n| n ** 2 }
puts squares.inspect
# [1, 4, 9, 16, 25]
# Reduce — accumulate
sum = numbers.reduce(0) { |acc, n| acc + n }
puts sum
# 15
# Find — first match
first_even = numbers.find { |n| n.even? }
puts first_even
# 2
Loop Performance Considerations
# Different ways to sum 1 to 1_000_000
# each — most idiomatic
sum = (1..1_000_000).reduce(:+)
# while — fastest for simple loops
i, sum = 0, 0
while i < 1_000_000
sum += i
i += 1
end
# times — clean for known counts
sum = 0
1_000_000.times { |i| sum += i }
Common Mistakes
1. Infinite Loops
# Wrong — no increment
count = 0
while count < 10
puts count
# count never changes!
end
# Right
count = 0
while count < 10
puts count
count += 1
end
2. Modifying Collection During Iteration
# Wrong — skips elements
arr = [1, 2, 3, 4]
arr.each do |n|
arr.delete(n)
end
# arr becomes [2, 4]
# Right — iterate over a copy
arr = [1, 2, 3, 4]
arr.dup.each do |n|
arr.delete(n)
end
# arr becomes []
3. Forgetting Break Condition in Loop
# Wrong — runs forever
loop do
puts "Running..."
end
# Right
loop do
puts "Running..."
break if condition_met?
end
4. Using For Instead of Each
# Less idiomatic
for fruit in fruits
puts fruit
end
# More idiomatic
fruits.each { |fruit| puts fruit }
5. Confusing Next and Break
[1, 2, 3].each do |n|
break if n == 2 # Stops iteration completely
puts n # Only prints 1
end
[1, 2, 3].each do |n|
next if n == 2 # Skips to next element
puts n # Prints 1 and 3
end
6. Scoping Issues with For
# for doesn't create a new scope
for i in 1..3
# ...
end
puts i # 3 — i leaks!
# each creates a new block scope
(1..3).each do |j|
# ...
end
puts j # NameError — j is undefined
Practice Questions
1. What's the difference between while and until?
while runs while the condition is truthy. until runs while the condition is falsy. until x == 5 is the same as while x != 5.
2. How do you skip to the next iteration in a loop?
Use next. It jumps to the next iteration without executing the rest of the block.
3. What does break do in a loop?
break exits the loop entirely, stopping all further iterations.
4. Why is each preferred over for in Ruby?
each is more idiomatic, creates a new scope (no variable leakage), and allows chaining with other Enumerable methods. for is a holdover from other languages and is rarely used in idiomatic Ruby.
Challenge: Write a Ruby program that uses loop with break, next, and redo to simulate a retry mechanism that attempts an operation up to 3 times, with a 1-second delay between retries.
Solution
def unreliable_operation(attempt)
puts "Attempt #{attempt}..."
# Simulate failure for attempts 1 and 2
attempt < 3 ? false : true
end
max_retries = 3
attempt = 1
loop do
result = unreliable_operation(attempt)
if result
puts "Operation succeeded!"
break
elsif attempt >= max_retries
puts "All attempts failed"
break
else
puts "Failed, retrying..."
attempt += 1
sleep 1
redo
end
end
FAQ
{{< faq question="Which loop should I use most of the time?" >}}
Use each for nearly everything. It's Ruby's most idiomatic loop and works with arrays, hashes, ranges, and any enumerable collection. Only use while/until when you need a condition-based loop without a collection.
{{< /faq >}}
{{< faq question="What's the difference between each and map?" >}}
each iterates and returns the original collection. map iterates and returns a new array with the transformed values. Use each for side effects and map for transformations.
{{< /faq >}}
{{< faq question="How do I loop infinitely?" >}}
Use loop do ... end with an explicit break condition. Ruby also supports while true or until false, but loop is the most idiomatic infinite loop.
{{< /faq >}}
{{< faq question="Can I break out of nested loops?" >}}
Yes. Use break to exit the innermost loop. Ruby doesn't have labeled breaks, so you may need to use a flag variable or catch/throw for deeply nested structures.
{{< /faq >}}
{{< faq question="What happens if I call next inside a times loop?" >}}
next works just like in any loop — it skips to the next iteration. The current iteration stops executing and the next one begins.
{{< /faq >}}
Try It Yourself
Run this comprehensive loop demonstration:
# loops_demo.rb
puts "=== each example ==="
[1, 2, 3].each { |n| print "#{n * 2} " }
puts
puts "=== while example ==="
count = 0
while count < 3
print "#{count} "
count += 1
end
puts
puts "=== until example ==="
count = 0
until count == 3
print "#{count} "
count += 1
end
puts
puts "=== times example ==="
3.times { |i| print "#{i} " }
puts
puts "=== upto example ==="
1.upto(5) { |i| print "#{i} " }
puts
puts "=== loop + break example ==="
count = 0
loop do
print "#{count} "
count += 1
break if count >= 3
end
puts
Expected output:
=== each example ===
2 4 6
=== while example ===
0 1 2
=== until example ===
0 1 2
=== times example ===
0 1 2
=== upto example ===
1 2 3 4 5
=== loop + break example ===
0 1 2
What's Next
Now that you understand loops, explore arrays in depth for storing and manipulating collections of data.
| Topic | Description | Link |
|---|---|---|
| Ruby Arrays | Array methods, map, select, reduce | {{< ref "06-arrays" >}} |
| Ruby Hashes | Hash syntax, symbol keys, merge | {{< ref "07-hashes" >}} |
| Python Loops | Compare Python iteration patterns | Python |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro