Array#rotate
rotate and rotate! shift array elements by moving the element at a given index to the front and wrapping the earlier items to the end. That makes the method useful whenever the order should advance without changing the meaning of the data.
Brief intro
rotate creates a new array with elements rearranged. rotate! modifies the original array in place. Both methods are useful for cycling through queue-like data structures or creating rotating schedules.
The method is a good fit any time the order should advance by one slot at a time. You can use it for round-robin turns, rotating menus, or any list that needs to treat the current first item as something that eventually moves to the back of the line. The behavior is simple, but it makes a lot of scheduling-style code easier to express.
The examples below move from the no-argument form to custom counts and in-place rotation. That progression is useful because the method does not change much between cases, but the effect on the source collection does, and that is the part readers usually need to keep straight.
Signature
| Method | Parameters | Return Type |
|---|---|---|
rotate | cnt = 1 (Integer) | Array |
rotate! | cnt = 1 (Integer) | Array (self) |
Parameter Details:
cnt— The zero-based index of the element that should become the first element. Default is1. Negative counts rotate in the opposite direction.
Code Examples
Basic Rotation
array = [1, 2, 3, 4, 5]
array.rotate
# => [2, 3, 4, 5, 1]
array
# => [1, 2, 3, 4, 5] - original unchanged
This first example shows the simplest form: the method returns a new array and leaves the original alone. That is the safest version when you want to inspect a rotated ordering without changing the source collection. The no-argument call rotates by one position, which is often the right default for round-robin patterns and simple shuffles.
Rotate with custom count
array = [1, 2, 3, 4, 5]
array.rotate(2)
# => [3, 4, 5, 1, 2]
array.rotate(0)
# => [1, 2, 3, 4, 5] - no rotation
array.rotate(5)
# => [1, 2, 3, 4, 5] - full cycle
Custom counts let you express the offset directly instead of calculating indexes by hand. A full-cycle rotation is still a useful check because it confirms that the method treats the array as circular rather than as a one-way shift.
Negative Rotation
array = [1, 2, 3, 4, 5]
array.rotate(-1)
# => [5, 1, 2, 3, 4]
array.rotate(-2)
# => [4, 5, 1, 2, 3]
Negative counts move the rotation in the opposite direction, which is handy when the item you want to expose is closer to the end than the front. The pattern is the same, but the sign of the count changes which side wraps around first. If you find yourself reaching for negative counts often, it may be worth checking whether reversing the input first keeps the logic simpler.
In-place rotation with rotate!
array = [1, 2, 3, 4, 5]
array.rotate!
# => [2, 3, 4, 5, 1]
array
# => [2, 3, 4, 5, 1] - original modified
array.rotate!(3)
# => [5, 1, 2, 3, 4]
array
# => [5, 1, 2, 3, 4]
This version is the one to use when the old ordering is no longer needed. Because the array changes in place, later code sees the new order without having to assign a separate variable. Round-robin scheduling is one of the most practical applications of this pattern, as the next example demonstrates.
Practical use: round-robin scheduling
players = ["Alice", "Bob", "Charlie", "Diana"]
# Generate schedule for 4 rounds
4.times do |round|
puts "Round #{round + 1}: #{players.join(' vs ')}"
players.rotate!
end
# Output:
# Round 1: Alice vs Bob vs Charlie vs Diana
# Round 2: Bob vs Charlie vs Diana vs Alice
# Round 3: Charlie vs Diana vs Alice vs Bob
# Round 4: Diana vs Alice vs Bob vs Charlie
Round-robin code is a good fit because the schedule advances by one turn at a time and the old order is not important once the next round starts. The bang variant keeps that progression compact and readable.
Return Value
rotatereturns a new array containing the rotated elements. The original array remains unchanged.rotate!returns self (the modified array), allowing method chaining.- When
cntequals the array length, the returned array equals the original.
That last detail is useful because it means rotation by the full length is a no-op. In practice, the method reads like a small state transition: move the front forward, keep the rest in order, and decide whether you want a copy or an in-place update.
When you are choosing between the two forms, think about whether the original ordering still matters after the rotation. If the answer is yes, use rotate; if the answer is no, rotate! keeps the code shorter.
Edge Cases
- Empty array returns a new empty array.
- Single-element array returns itself (new array for
rotate, same forrotate!).
[].rotate
# => []
[].rotate!
# => []
[42].rotate
# => [42]
[42].rotate!
# => [42]
The edge cases stay boring in a good way, which is exactly what you want from a helper like this. Empty arrays stay empty, and single-element arrays keep the same visible order because there is nowhere meaningful to rotate.