Enumerable#cycle
#cycle repeats the elements of an array (or any enumerable) indefinitely, or for a specific number of times. It is useful for round-robin scheduling, rotating through options, and generating repeating patterns without writing manual loops.
Basic Usage
When called with a block, #cycle yields each element in order, then starts over from the beginning:
[:gold, :silver, :bronze].cycle { |medal| puts medal }
# gold
# silver
# bronze
# gold
# silver
# bronze
# ... forever until interrupted
This will run forever unless you explicitly break out of it.
Cycling a Specific Number of Times
Pass an integer to #cycle(n) to repeat the collection exactly n times:
[:left, :right].cycle(3) { |direction| puts direction }
# left
# right
# left
# right
# left
# right
After completing the specified number of cycles, the loop ends and #cycle returns nil.
Breaking Out of a Cycle
Use break to stop early, just like any other loop:
result = []
[1, 2, 3].cycle.take(5) { |n| result << n }
result
# => [1, 2, 3, 1, 2]
The #take method here limits the iteration to 5 elements before the cycle is exhausted.
Without a Block — Returns an Enumerator
Called without a block, #cycle returns an Enumerator so you can chain other Enumerable methods:
enum = [1, 2, 3].cycle
enum.take(5)
# => [1, 2, 3, 1, 2]
enum = [:a, :b, :c].cycle(2)
enum.to_a
# => [:a, :b, :c, :a, :b, :c]
Practical Examples
Round-Robin Scheduling
Assign tasks to workers in rotation:
workers = [:alice, :bob, :carol]
tasks = [:upload, :process, :deliver, :archive, :notify]
assignments = tasks.zip(workers.cycle)
assignments
# => [[:upload, :alice], [:process, :bob], [:deliver, :carol],
# [:archive, :alice], [:notify, :bob]]
Rotating Menus
Display items in a repeating carousel:
menu = ["Breakfast", "Lunch", "Dinner"]
menu.cycle.take(7)
# => ["Breakfast", "Lunch", "Dinner", "Breakfast", "Lunch", "Dinner", "Breakfast"]
Generating a Repeating Pattern
# Create 12 months organized by quarter
quarters = ["Q1", "Q2", "Q3", "Q4"]
quarters.cycle.take(12)
# => ["Q1", "Q2", "Q3", "Q4", "Q1", "Q2", "Q3", "Q4", "Q1", "Q2", "Q3", "Q4"]
Using cycle with break
result = []
[10, 20, 30].cycle do |n|
break if n == 30
result << n
end
result
# => [10, 20]
Performance Notes
cyclecreates an Enumerator lazily. No array duplication occurs until you consume it.- Calling
cyclewith no argument on a large or infinite collection withoutbreakortakewill consume all available memory eventually. - For a known number of iterations, always pass that count:
cycle(n)instead ofcycle { ... }with a manual counter. It avoids the overhead of checking a break condition on every iteration. - Chaining
#cyclewith#first(n)or#take(n)is more idiomatic and safer than relying onbreakinside the block.
Return Value
| Form | Return Value |
|---|---|
| `collection.cycle { | x |
| `collection.cycle(n) { | x |
collection.cycle (no block) | Enumerator |
collection.cycle(n) (no block) | Enumerator |
See Also
- /reference/enumerable/enumerable-take/ — limit iterations by taking a set number of elements
- /reference/enumerable/enumerable-reduce/ — aggregate values across iterations
- /reference/enumerable/enumerable-uniq/ — remove duplicates from an enumerable