299

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)?

sfletche
  • 42,426
  • 25
  • 94
  • 115
  • 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

14 Answers14

592

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,707
  • 11
  • 19
Arun P Johny
  • 376,738
  • 64
  • 519
  • 520
  • Can you please explaing slice() with no parameters? – user786 Jun 03 '15 at 03:53
  • 7
    @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
  • 3
    I think @Alex meant 'explain it in your answer'...regardless...Brilliant solution. Thanks! – sfletche Jun 03 '15 at 03:56
  • 49
    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
  • 6
    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
  • 3
    @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
186

In ES6:

const newArray = [...array].reverse()
Brian M. Hunt
  • 76,464
  • 71
  • 217
  • 333
  • 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
  • 12
    I'm consistently getting `.slice` as significantly faster. – James Coyle Jan 31 '19 at 15:58
  • 8
    @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
  • 1
    See https://stackoverflow.com/a/52929821/1480587 about benchmarks of different versions for dupicating an array. – Peter T. Nov 12 '21 at 21:32
13

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
  • 34,173
  • 19
  • 88
  • 85
  • 4
    `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
  • 1
    @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
7

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
  • 79
  • 1
  • 2
6

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 Splice
var reverseArray1 = array.splice().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
  • 3,202
  • 5
  • 22
  • 30
5

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]), []);
hanswilw
  • 134
  • 1
  • 4
5

const newArr = Array.from(originalArray).reverse();
Raghu
  • 2,540
  • 2
  • 30
  • 36
3
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,034
  • 33
  • 44
1

While array.slice().reverse() is what I would myself go for in a situation where I cannot use a library, it's not so good in terms of readability: we are using imperative logic that the person reading the code must think through. Considering also that there is the same problem with sort, there's a solid justification here for using a utility library.

You can use a function pipe from fp-ts or a library I've written myself. It pipes a value though a number of functions, so pipe(x, a, b) is equivalent to b(a(x)). With this function, you can write

pipe(yourArray, reverseArray)

where reverseArray is a function that basically does .slice().reverse(), i.e. reverses the array immutably. Generally speaking, pipe lets you do the equivalent of dot-chaining, but without being limited to the methods available on the array prototype.

Ivan
  • 1,237
  • 12
  • 20
1

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
  • 1,646
  • 1
  • 15
  • 26
1

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]
Rohìt Jíndal
  • 16,572
  • 12
  • 64
  • 110
-1

INTO plain Javascript:

function reverseArray(arr, num) {
  var newArray = [];
  for (let i = num; i <= arr.length - 1; i++) {
    newArray.push(arr[i]);
  }

  return newArray;
}
Amit kumar
  • 415
  • 6
  • 13
-6

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 Harris Feb 20 '18 at 21:02
  • 6
    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