Skip to main content
View
Chapter 6 / 16Loops and iterators
1. Variables and data types
2. Strings and symbols
3. Arrays
4. Hashes
5. Conditionals
6. Loops and iterators
7. Methods and blocks
8. Classes and objects
9. Inheritance
10. Modules
11. Extending code with prepend
12. Enumerable
13. Where to go next
14. Error handling
15. Reading and writing files
16. Advanced class features

Loops and iterators

This chapter introduces the tools that allow you to repeat instructions: classic loops and iterators. We also discover how to read what the user types on the keyboard.

Principle

We already saw .each in chapter 3 for iterating over an Array. But sometimes, we need to repeat code without having a collection: waiting for a user action, simulating combat turns, or displaying a countdown.

Ruby provides two families of tools:

  • Loops (while, until, loop): repeat a block as long as a condition is true (or until we decide to stop). Useful when we don't know in advance how many times we'll loop.
  • Iterators (times, upto, downto, each): iterate a fixed number of times or over a collection. This is the idiomatic Ruby way.

Reading user input

Before building menus, we need to know how to read what the user types. Ruby provides gets:

print 'What is your name? '
name = gets.chomp
puts "Hello, #{name}!"
  • print displays text without a newline (unlike puts). The cursor stays on the same line, which is convenient for questions.
  • gets waits for the user to type something and press Enter. It returns the text as a String.
  • .chomp removes the trailing newline \n. Without .chomp, the String would contain an invisible line break.

To read a number, add .to_i:

print 'Pokémon level: '
level = gets.chomp.to_i
puts "Level: #{level}"
  • .to_i converts the String to an Integer. If the user types something other than a number, .to_i returns 0.

The while loop

while repeats a block as long as the condition is true:

level = 1

while level < 5
puts "Pikachu is at level #{level}"
level += 1
end

Outputs:

Pikachu is at level 1
Pikachu is at level 2
Pikachu is at level 3
Pikachu is at level 4
  • The condition level < 5 is evaluated before each iteration. When level reaches 5, the condition becomes false and the loop stops.
  • level += 1 is essential. Without it, level would stay at 1 and the loop would run forever (infinite loop). If that happens, press Ctrl+C to interrupt the program.

The until loop

until is the opposite of while: it repeats as long as the condition is false (in other words, until the condition becomes true):

hp = 50
max_hp = 120

until hp >= max_hp
hp += 10
puts "Healing... HP: #{hp}/#{max_hp}"
end

puts 'Healing complete!'
  • until hp >= max_hp reads as: "repeat until the HP reach the maximum".
  • until is sometimes more readable than while when thinking in terms of "until".

The loop loop

loop creates an infinite loop. You exit it with break:

loop do
print 'Type "quit" to exit: '
input = gets.chomp

break if input == 'quit'

puts "You typed: #{input}"
end

puts 'Exited the loop!'
  • loop do ... end runs indefinitely until a break is reached.
  • break if input == 'quit' exits the loop when the user types "quit".
  • This is the ideal pattern for interactive menus: display the options, read the choice, process it, and start again.

The times iterator

times executes a block a fixed number of times:

3.times do
puts 'Pikachu uses Thunderbolt!'
end

You can also retrieve the iteration number:

5.times do |turn|
puts "Turn #{turn + 1}"
end

Outputs:

Turn 1
Turn 2
Turn 3
Turn 4
Turn 5
  • |turn| receives the iteration number, starting at 0. We add 1 for human-readable display.
  • times is perfect when you know the number of repetitions in advance.

The upto and downto iterators

To iterate over a range of numbers:

# Count from 1 to 5
1.upto(5) do |level|
puts "Level #{level}"
end
# Countdown
5.downto(1) do |count|
puts "#{count}..."
end
puts 'Evolution!'
  • 1.upto(5) iterates from 1 to 5 inclusive, in ascending order.
  • 5.downto(1) iterates from 5 to 1 inclusive, in descending order.

Reminder: each and each_with_index

We already saw each in chapter 3. It is the iteration method in Ruby:

team = ['Pikachu', 'Charizard', 'Blastoise']

team.each { |pokemon| puts "Go, #{pokemon} !" }

team.each_with_index do |pokemon, index|
puts "#{index + 1}. #{pokemon}"
end

To iterate over a Hash (seen in chapter 4), the block receives the key and the value:

stats = { hp: 35, attack: 55, defense: 40 }

stats.each do |stat, value|
puts "#{stat} : #{value}"
end

The for loop

for exists in Ruby but is almost never used. We mention it because you may encounter it in older code:

types = [:fire, :water, :grass]

for type in types
puts "Type : #{type}"
end
  • for is syntactic sugar for each, but with a drawback: the variable type continues to exist after the loop. With each, the block variable is local. That is why Ruby developers prefer each.

Flow control: break and next

break -- exit the loop

break exits the loop immediately:

team = ['Pikachu', 'Charizard', 'Blastoise', 'Venusaur']

team.each do |pokemon|
break if pokemon == 'Blastoise'

puts "Checking #{pokemon}..."
end

puts 'Blastoise found!'

Outputs:

Checking Pikachu...
Checking Charizard...
Blastoise found!
  • As soon as pokemon equals 'Blastoise', break stops the each. The remaining elements are not iterated.

next -- skip to the next iteration

next skips the rest of the block and moves to the next element:

levels = [5, 0, 12, 0, 8]

levels.each do |level|
next if level == 0

puts "Level #{level} Pokémon"
end

Outputs:

Level 5 Pokémon
Level 12 Pokémon
Level 8 Pokémon
  • next if level == 0 skips Pokemon with level 0 (fainted). The puts is not executed for them, but the loop continues with the remaining elements.

Conclusion

  • while repeats as long as a condition is true. until repeats until it becomes true.
  • loop with break is the standard pattern for interactive menus.
  • times repeats a fixed number of times. upto/downto iterate over a range.
  • each is the idiomatic method for iterating over collections. Prefer each over for.
  • break exits the loop. next skips to the next iteration.
  • gets.chomp reads user input. .to_i converts it to a number.
  • print displays text without a newline (useful for questions).