Array#rotate
Array#rotate returns a new array with all elements shifted cyclically to the left until the element at the given offset becomes the first element. It is useful for cycling through elements in a circular fashion — the last element wraps around to the back.
Basic Usage
fruits = ["apple", "banana", "cherry", "date"]
rotated = fruits.rotate
puts rotated # => ["banana", "cherry", "date", "apple"]
puts fruits # => ["apple", "banana", "cherry", "date"]
By default, rotate uses an offset of 1, shifting the first element to the end.
Using an Offset
Pass an integer to control which element becomes first:
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
# Positive offset: count from the left
puts days.rotate(2) # => ["Wednesday", "Thursday", "Friday", "Monday", "Tuesday"]
# Negative offset: count from the right
puts days.rotate(-1) # => ["Friday", "Monday", "Tuesday", "Wednesday", "Thursday"]
# Offset of 0: no rotation
puts days.rotate(0) # => ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
rotate vs shift
Both rotate and shift involve the first element, but they behave differently:
rotatemoves the first element to the end, returning a new array — the original is unchangedshiftremoves the first element entirely, reducing the array’s length
queue = ["first", "second", "third"]
rotated = queue.rotate
puts rotated # => ["second", "third", "first"]
puts queue # => ["first", "second", "third"] — unchanged
shifted = queue.shift
puts shifted # => "first"
puts queue # => ["second", "third"] — element removed
In-Place Rotation with rotate!
The bang variant rotate! rotates the array in place, modifying the original:
order = ["A", "B", "C", "D"]
order.rotate!
puts order # => ["B", "C", "D", "A"]
Carousel Cycling
rotate is ideal for cycling through items like a carousel — each call advances to the next item:
slides = ["intro", "features", "pricing", "contact"]
current = slides.rotate
puts "Showing: #{current.first}" # => "Showing: features"
current = current.rotate
puts "Showing: #{current.first}" # => "Showing: pricing"
current = current.rotate
puts "Showing: #{current.first}" # => "Showing: contact"
current = current.rotate
puts "Showing: #{current.first}" # => "Showing: intro"
Round-Robin Scheduling
Distribute tasks or games in a round-robin fashion:
teams = ["Red", "Blue", "Green", "Yellow"]
# Generate round 1 pairings
round1 = teams.rotate
pairings = round1.each_slice(2).to_a
puts pairings # => [["Blue", "Green"], ["Yellow", "Red"]]
# Advance for round 2
round2 = round1.rotate
pairings = round2.each_slice(2).to_a
puts pairings # => [["Green", "Yellow"], ["Red", "Blue"]]
Rotating Work Schedules
Assign rotating shifts fairly across team members:
staff = ["Alice", "Bob", "Carol", "Diana"]
def assign_shift(staff, week)
rotated = staff.rotate(week - 1)
rotated[0]
end
puts "Week 1: #{assign_shift(staff, 1)}" # => "Week 1: Bob"
puts "Week 2: #{assign_shift(staff, 2)}" # => "Week 2: Carol"
puts "Week 3: #{assign_shift(staff, 3)}" # => "Week 3: Diana"
puts "Week 4: #{assign_shift(staff, 4)}" # => "Week 4: Alice"
Return Value
rotatereturns a new array with elements rotated; the original is unchangedrotate!returns the same array (self) after rotating in place
letters = ["a", "b", "c"]
new_arr = letters.rotate
puts new_arr.class # => Array
puts letters.class # => Array (unchanged)
same_arr = letters.rotate!
puts same_arr.class # => Array (same object)
puts same_arr.equal?(letters) # => true
Performance Note
rotate has O(n) time complexity because it creates a new array and copies all elements. The space complexity is also O(n) due to the new array allocation.
rotate! has the same time complexity but avoids allocation since it modifies the array in place (O(n) time, O(1) extra space).
See Also
- /reference/array-methods/array-shift/ — remove and return the first element
- /reference/array-methods/array-pop/ — remove and return the last element
- /reference/enumerable/enumerable-cycle/ — loop over elements repeatedly