Reversing an Array (Iterative, Recursive, Copy)
Reversing an array is a one-liner if you do not mind mutating the input, and a slightly different one-liner if you do. This snippet covers the built-in `reverse()` mutating call alongside the immutable `toReversed()` and spread alternatives, the two-pointer in-place swap that interviewers ask about, and a recursive form for the recursion lesson. Production code should pick one of the first-accordion forms; the others are for understanding.
705 views
8
// Mutating: Array.prototype.reverse() flips in place and returns the same array.
const nums = [1, 2, 3, 4, 5];
nums.reverse();
console.log(nums); // [5, 4, 3, 2, 1] (original mutated)
// Immutable via spread: copy first, then reverse the copy.
const original = ['a', 'b', 'c', 'd'];
const reversed = [...original].reverse();
console.log(original); // ['a', 'b', 'c', 'd'] (untouched)
console.log(reversed); // ['d', 'c', 'b', 'a']
// Immutable via Array.prototype.toReversed() (Node 20+, ES2023).
const src = [10, 20, 30];
console.log(src.toReversed()); // [30, 20, 10]
console.log(src); // [10, 20, 30]Array.prototype.reverse() reverses in place and returns the same array, so nums.reverse() and [...nums].reverse() look almost identical but have very different semantics: the first form mutates the original and the second leaves it untouched. The newer toReversed() (ES2023, Node 20+) returns a reversed copy in one call, which is more declarative. Default to toReversed() if your runtime supports it; otherwise [...arr].reverse() is the immutable fallback. Reach for the bare reverse() only when you specifically want the mutation.
// Educational version that shows what reverse() does internally.
const reverseInPlace = (arr) => {
let left = 0;
let right = arr.length - 1;
while (left < right) {
// Destructuring swap, no temp variable.
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
return arr;
};
console.log(reverseInPlace([1, 2, 3, 4, 5])); // [5, 4, 3, 2, 1]
console.log(reverseInPlace(['a', 'b', 'c'])); // ['c', 'b', 'a']
console.log(reverseInPlace([42])); // [42]
console.log(reverseInPlace([])); // []Two pointers walk inward from each end and swap until they meet. Time complexity is O(n) and space is O(1), which matches the built-in reverse(). The destructuring swap ([a[i], a[j]] = [a[j], a[i]]) avoids a temporary variable and is idiomatic in modern JS. Edge cases handled correctly: arrays of length 0 or 1 skip the loop body entirely because left < right is false from the start. This pattern shows up in interviews and in cousin algorithms (palindrome check, partitioning a Quicksort range).
// Reverse by peeling the head and recursing on the tail.
const reverseRecursive = (arr) => {
if (arr.length <= 1) return arr;
return [...reverseRecursive(arr.slice(1)), arr[0]];
};
console.log(reverseRecursive([1, 2, 3, 4, 5])); // [5, 4, 3, 2, 1]
console.log(reverseRecursive(['a', 'b', 'c'])); // ['c', 'b', 'a']
console.log(reverseRecursive([])); // []The recursive form treats reversal as "reverse the tail, then put the head at the end." The base case is an array of length 0 or 1, which is its own reverse. Each step calls slice(1) to peel off the head and spreads the recursed tail before appending the head. This is great for teaching recursion but it is O(n^2) time and O(n) extra memory because every step copies the tail; do not use it on long arrays. For production reverse, stick to the built-in reverse() or toReversed() from accordion 1.
