357

Array.prototype.reverse reverses the contents of an array in place (with mutation)...

Is there a similarly simple strategy for reversing an array without altering the contents of the original array (without mutation)?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sfletche
  • 47,248
  • 30
  • 103
  • 119
  • 1
    possible duplicate of [Making an independent copy of a reversed array in JavaScript](http://stackoverflow.com/q/23666679/1048572) – Bergi Jun 03 '15 at 03:59
  • Here's a benchmark comparing the different options: http://jsben.ch/HZ7Zp – Solomon Ucko Jun 13 '19 at 18:55

15 Answers15

671

You can use slice() to make a copy then reverse() it

var newarray = array.slice().reverse();

var array = ['a', 'b', 'c', 'd', 'e'];
var newarray = array.slice().reverse();

console.log('a', array);
console.log('na', newarray);
Florian Sowade
  • 1,727
  • 12
  • 20
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • Can you please explaing slice() with no parameters? – user786 Jun 03 '15 at 03:53
  • 9
    @Alex [slice](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) - `If begin is omitted, slice begins from index 0.` - so it is the same as `array.slice(0)` – Arun P Johny Jun 03 '15 at 03:55
  • 4
    I think @Alex meant 'explain it in your answer'...regardless...Brilliant solution. Thanks! – sfletche Jun 03 '15 at 03:56
  • 65
    I would NOT recommend to use this approach, because it's very misleading practice. It’s very confusing when you use slice() and you actually not slicing anything. If you need immutable reverse, just create fresh new copy: ***const newArray = [...array].reverse()*** – Oleg Matei Jun 27 '19 at 21:11
  • 11
    Can we all just take a moment to identify how much further JavaScript needs to grow? This is the best we've got? Cmon JavaScript! grow up a little. – Gant Laborde Aug 30 '20 at 04:28
  • 7
    @OlegMatei Slicing the whole array is still slicing. It's not confusing if you understand what `Array#slice` does. There's no reason spread syntax should be any less confusing. – Unmitigated Oct 14 '21 at 01:44
  • @OlegMatei I understand your point about misleading code. However, `slice()` still seems to be the fastest option, see https://stackoverflow.com/a/52929821/1480587 – Peter T. Nov 12 '21 at 21:32
218

In ES6:

const newArray = [...array].reverse()
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • 2
    How does the performance of this compare with that of the accepted answer? Are they the same? – Katie Jan 30 '19 at 19:47
  • 16
    I'm consistently getting `.slice` as significantly faster. – James Coyle Jan 31 '19 at 15:58
  • 10
    @JamesCoyle It's probably browser and OS dependent, but `slice` is likely to be faster b/c `[...]` is a generic iterable-to-array so cannot make as many assumptions. As well, it's likely that `slice` is better optimized b/c it's been around a long time. – Brian M. Hunt Jan 31 '19 at 17:39
  • My thoughts exactly. – James Coyle Jan 31 '19 at 17:41
  • 2
    See https://stackoverflow.com/a/52929821/1480587 about benchmarks of different versions for dupicating an array. – Peter T. Nov 12 '21 at 21:32
19

const originalArray = ['a', 'b', 'c', 'd', 'e', 'f'];

const newArray = Array.from(originalArray).reverse();

console.log(newArray);
Raghu
  • 2,678
  • 2
  • 31
  • 38
  • I like this because the intent is more explicit. Its not obvious that `slice()` creates a duplicate array. – icc97 Aug 23 '23 at 11:37
  • You can read it as *"Create a new array from `originalArray` then reverse it"* – icc97 Aug 23 '23 at 12:02
16

Another ES6 variant:

We can also use .reduceRight() to create a reversed array without actually reversing it.

let A = ['a', 'b', 'c', 'd', 'e', 'f'];

let B = A.reduceRight((a, c) => (a.push(c), a), []);

console.log(B);

Useful Resources:

Mohammad Usman
  • 37,952
  • 20
  • 92
  • 95
  • 7
    `reduceRight` is slow af – Dragos Rizescu Mar 25 '18 at 09:15
  • @DragosRizescu Can you share some sample test results? – Mohammad Usman Mar 26 '18 at 05:14
  • 1
    Here is a repo that you can play with: (not the best example, but had this argument with someone a while ago in a React context, thus I've put it together): https://github.com/rizedr/reduce-vs-reduceRight – Dragos Rizescu Mar 27 '18 at 15:04
  • 4
    `(a, c) => a.concat([c])` feels more idiomatic than `(a, c) => (a.push(c), a)` – Kyle Lin Dec 20 '18 at 23:42
  • 2
    @DragosRizescu it actually looks like this is the fastest of the 3 top answers. https://jsperf.com/reverse-vs-slice-reverse/3 – DBosley Sep 06 '19 at 14:45
  • @KyleLin It's a much slower though. The really idiomatic solution would be not to use a concise arrow function but `(a, c) => { a.push(c); return a; }` – Bergi Dec 18 '19 at 20:25
  • @Unmitigated Yes, most of the time. But if `c` is an array, then the resulting behavior will be different than what the OP intended. – Kyle Lin Oct 14 '21 at 05:16
  • That Comma operator is kind of crazy cool and taught me something and the `reduceRight` is definitely a strong part of functional programming - but it makes the code really confusing to read. Performant or not, I'd rather the [@Ragu's explicit](https://stackoverflow.com/a/71475972/327074) `const B = Array.from(A).reverse()` than the three or four levels of understanding it takes for your `reduceRight`. – icc97 Aug 23 '23 at 11:59
8

Try this recursive solution:

const reverse = ([head, ...tail]) => 
    tail.length === 0
        ? [head]                       // Base case -- cannot reverse a single element.
        : [...reverse(tail), head]     // Recursive case

reverse([1]);               // [1]
reverse([1,2,3]);           // [3,2,1]
reverse('hello').join('');  // 'olleh' -- Strings too!                              
Austin Keeton
  • 97
  • 1
  • 2
7

There are multiple ways of reversing an array without modifying. Two of them are

var array = [1,2,3,4,5,6,7,8,9,10];

// Using slice
var reverseArray1 = array.slice().reverse(); // Fastest

// Using spread operator
var reverseArray2 = [...array].reverse();

// Using for loop 
var reverseArray3 = []; 
for(var i = array.length-1; i>=0; i--) {
  reverseArray.push(array[i]);
}

Performance test http://jsben.ch/guftu

shekhardtu
  • 4,335
  • 7
  • 27
  • 34
6

An ES6 alternative using .reduce() and spreading.

const foo = [1, 2, 3, 4];
const bar = foo.reduce((acc, b) => ([b, ...acc]), []);

Basically what it does is create a new array with the next element in foo, and spreading the accumulated array for each iteration after b.

[]
[1] => [1]
[2, ...[1]] => [2, 1]
[3, ...[2, 1]] => [3, 2, 1]
[4, ...[3, 2, 1]] => [4, 3, 2, 1]

Alternatively .reduceRight() as mentioned above here, but without the .push() mutation.

const baz = foo.reduceRight((acc, b) => ([...acc, b]), []);
warlo
  • 159
  • 1
  • 4
5

ES2023 Array Method toReversed()

The toReversed() method transposes the elements of the calling array object in reverse order and returns a new array.

const items = [1, 2, 3];
console.log(items); // [1, 2, 3]

const reversedItems = items.toReversed();
console.log(reversedItems); // [3, 2, 1]
console.log(items); // [1, 2, 3]

Browser compatibility

enter image description here

Danziger
  • 19,628
  • 4
  • 53
  • 83
Talha Tayyab
  • 8,111
  • 25
  • 27
  • 44
3

There's a new tc39 proposal, which adds a toReversed method to Array that returns a copy of the array and doesn't modify the original.

Example from the proposal:

const sequence = [1, 2, 3];
sequence.toReversed(); // => [3, 2, 1]
sequence; // => [1, 2, 3]

As it's currently in stage 3, it will likely be implemented in browser engines soon, but in the meantime a polyfill is available here or in core-js.

Josh
  • 2,324
  • 1
  • 21
  • 29
2
const arrayCopy = Object.assign([], array).reverse()

This solution:

-Successfully copies the array

-Doesn't mutate the original array

-Looks like it's doing what it is doing

1

Reversing in place with variable swap just for demonstrative purposes (but you need a copy if you don't want to mutate)

const myArr = ["a", "b", "c", "d"];
const copy = [...myArr];
for (let i = 0; i < (copy.length - 1) / 2; i++) {  
    const lastIndex = copy.length - 1 - i; 
    [copy[i], copy[lastIndex]] = [copy[lastIndex], copy[i]] 
}

daino3
  • 4,386
  • 37
  • 48
1

Jumping into 2022, and here's the most efficient solution today (highest-performing, and no extra memory usage).


For any ArrayLike type, the fastest way to reverse is logically, by wrapping it into a reversed iterable:

function reverse<T>(input: ArrayLike<T>): Iterable<T> {
    return {
        [Symbol.iterator](): Iterator<T> {
            let i = input.length;
            return {
                next(): IteratorResult<T> {
                    return i
                        ? {value: input[--i], done: false}
                        : {value: undefined, done: true};
                },
            };
        },
    };
}

This way you can reverse-iterate through any Array, string or Buffer, without any extra copy or processing for the reversed data:

for(const a of reverse([1, 2, 3])) {
    console.log(a); //=> 3 2 1
}

It is the fastest approach, because you do not copy the data, and do no processing at all, you just reverse it logically.

vitaly-t
  • 24,279
  • 15
  • 116
  • 138
0

Is there a similarly simple strategy for reversing an array without altering the contents of the original array (without mutation) ?

Yes, there is a way to achieve this by using to[Operation] that return a new collection with the operation applied (This is currently at stage 3, will be available soon).

Implementation will be like :

const arr = [5, 4, 3, 2, 1];

const reversedArr = arr.toReverse();

console.log(arr); // [5, 4, 3, 2, 1]

console.log(reversedArr); // [1, 2, 3, 4, 5]
Debug Diva
  • 26,058
  • 13
  • 70
  • 123
  • `Uncaught TypeError: arr.toReverse is not a function` `toReverse` is not a thing in JavaScript. – Cerbrus Sep 15 '22 at 08:23
  • 1
    @Cerbrus not a thing *yet* - it's still [a proposal](https://github.com/tc39/proposal-change-array-by-copy) which is in the last stage before being finalised. Polyfills are available to preview the functionality: [for example here](https://github.com/zloirock/core-js#change-array-by-copy). – VLAZ Sep 15 '22 at 08:30
0

Not the best solution but it works

Array.prototype.myNonMutableReverse = function () {
  const reversedArr = [];
  for (let i = this.length - 1; i >= 0; i--) reversedArr.push(this[i]);
  return reversedArr;
};

const a = [1, 2, 3, 4, 5, 6, 7, 8];
const b = a.myNonMutableReverse();
console.log("a",a);
console.log("////////")
console.log("b",b);
General Grievance
  • 4,555
  • 31
  • 31
  • 45
-9

es6:

const reverseArr = [1,2,3,4].sort(()=>1)
  • 5
    Welcome to SO, Radion! When leaving an answer it's typically a good idea to explain why your answer works and why you came to this conclusion, this helps newer users with understanding the interactions and languages you have specified. – Ethan Field Sep 19 '17 at 14:07
  • In Radion's defense :P that `1` at the end could have been anything greater than zero, because that's how `Array.prototype.sort` callback (or so called `compare function`) works. Basically you always compare 2 numbers and in this case the comparison returns always positive so it says always move to the second number in front of the first one :) this is very explanatory: https://stackoverflow.com/questions/6567941/how-does-sort-function-work-in-javascript-along-with-compare-function – iulial Feb 18 '18 at 19:21
  • 9
    `sort()` mutates the array (i.e., sorts it in place), which is what the OP wants to avoid. – clint Feb 20 '18 at 21:02
  • 8
    This answer is wrong in several ways. (1) it mutates the array, as @ClintHarris points out, so it's no better than .reverse(). (2) your comparator is illegal-- when you return 1 for a,b, you must return a negative number for b,a. If your answer reverses the array on some implementations, it's entirely by luck. – Don Hatch May 30 '18 at 06:51
  • Please test code when submitting it. At the very least it should do the required task, even if it does mutate the array. This just doesn't work as intended (it doesn't change the array at all) ([demo](https://tio.run/##y0osSyxOLsosKNHNy09J/f8/OT@vOD8nVS8nP10j2lDHSMdYxyRWrzi/qERDQ9PWzlBT8/9/AA)) and is easily corrected with the addition of a single character. – General Grievance Aug 21 '23 at 20:02