517

Let's suppose I wanted a sort function that returns a sorted copy of the inputted array. I naively tried this

function sort(arr) {
 return arr.sort();
}

and I tested it with this, which shows that my sort method is mutating the array.

var a = [2,3,7,5,3,7,1,3,4];
sort(a);
alert(a); //alerts "1,2,3,3,3,4,5,7,7"

I also tried this approach

function sort(arr) {
 return Array.prototype.sort(arr);
}

but it doesn't work at all.

Is there a straightforward way around this, preferably a way that doesn't require hand-rolling my own sorting algorithm or copying every element of the array into a new one?

danronmoon
3,8735 gold badges36 silver badges58 bronze badges
asked Mar 6, 2012 at 22:11
5
  • 1
    create a deep copy of the array and sort it instead. Commented Mar 6, 2012 at 22:12
  • 2
    @evanmcdonnal A shallow copy might be good enough if all is wanted is a reordering and not a duplicate of every item in the array. Commented Mar 6, 2012 at 22:14
  • .sort requires the this value to be the array, so for the last snippet to work you would do .sort.call(arr) (though it doesn't solve your problem). Commented Mar 6, 2012 at 22:15
  • @Kekoa Yeah that's a good point. There is no need to consume more memory if you're only going to change the order of the elements and not the elements themselves. Commented Mar 6, 2012 at 22:16
  • zzzzBov's method is working like a charm! stackoverflow.com/a/9592774/7011860 Commented Jun 27, 2019 at 6:14

12 Answers 12

558

With the introduction of the new .toSorted method in JavaScript, there's now a straightforward way to get a sorted copy of the array without modifying the original array:

const sorted = arr.toSorted();

For more details, you can refer to the MDN documentation on .toSorted.

Note: Before using .toSorted, make sure to check the compatibility with your environment. This method requires Node.js >= 20.0.0 or a recent version of modern browsers. If you are working in an environment that does not support this version, you may need to use the older method below.


For completeness, here's the older method using ES6 spread syntax to create a copy before sorting:

const sorted = [...arr].sort();

The spread-syntax as array literal (copied from MDN):

const arr = [1, 2, 3];
const arr2 = [...arr]; // like arr.slice()
answered Feb 24, 2017 at 15:52
9
  • 1
    this is really great.i think easier to understand than the concat and other approaches Commented Aug 31, 2020 at 20:02
  • 6
    To those saying it's not valid JavaScript... it's perfectly valid. If you're in Chrome/Safari/Edge or Firefox: open the dev console, define an array called arr and paste the expression to see the result. Commented Jun 17, 2021 at 7:48
  • 3
    @Cerin It sounds like you are on an incredibly outdated version of JS. Commented Sep 10, 2021 at 15:31
  • 1
    Is this method faster than .slice() to get a copy of the array? Commented Apr 28, 2022 at 6:46
  • 2
    the .toSorted() method on Array now exists and can be used instead. Commented Aug 2, 2024 at 11:33
259

Just copy the array. There are many ways to do that:

function sort(arr) {
 return arr.concat().sort();
}
// Or:
return Array.prototype.slice.call(arr).sort(); // For array-like objects
answered Mar 6, 2012 at 22:13
12
  • 4
    Will this do a deep copy, i.e., will nested objects and arrays also be copied? Commented Mar 6, 2012 at 22:15
  • 3
    Is there any advantage to using concat over say slice(0) or are they all pretty much just the same? Commented Mar 6, 2012 at 22:15
  • 8
    @PeterOlson No, it's a shallow copy. If you really want a deep copy, use the search feature on Stack Overflow to find existing excellent answers for that. Commented Mar 6, 2012 at 22:19
  • 19
    Slice is now reported as notably faster Commented May 31, 2017 at 13:51
  • 7
    why Array.prototype.slice.call(arr).sort(); instead of arr.slice().sort(); ? Commented Oct 25, 2019 at 19:08
82

Try the following

function sortCopy(arr) { 
 return arr.slice(0).sort();
}

The slice(0) expression creates a copy of the array starting at element 0.

answered Mar 6, 2012 at 22:13
0
58

You can use slice with no arguments to copy an array:

var foo,
 bar;
foo = [3,1,2];
bar = foo.slice().sort();
answered Mar 6, 2012 at 22:14
0
25

ES2023 Array Method toSorted():

The toSorted() method of Array instances is the copying version of the sort() method. It returns a new array with the elements sorted in ascending order.

const arr = [2, 1, 3];
const arrSorted = arr.toSorted();
console.log(arr); //[2, 1, 3]
console.log(arrSorted); //[1, 2, 3]

answered Jun 9, 2023 at 21:20
2
  • Property 'toSorted' does not exist on type 'unknown[]'.ts(2339) Commented May 20, 2024 at 10:45
  • Mind blown, finally! Commented Jul 18, 2024 at 8:55
21

You can also do this

d = [20, 30, 10]
e = Array.from(d)
e.sort()

This way d will not get mutated.

function sorted(arr) {
 temp = Array.from(arr)
 return temp.sort()
}
//Use it like this
x = [20, 10, 100]
console.log(sorted(x))
answered Jan 7, 2018 at 6:48
0
18

Update - Array.prototype.toSorted() proposal

The Array.prototype.toSorted(compareFn) -> Array is a new method that was proposed to be added to the Array.prototype and is currently in stage 3 (Soon to be available).

This method will keep the target Array untouched and return a copy with the change performed instead.

answered Jun 2, 2022 at 11:23
5

Remember that if no argument is passed to sort, it will not properly sort numbers by value. For numbers, try this. This does not mutate the original array.

function sort(arr) {
 return arr.slice(0).sort((a,b) => a-b);
}
General Grievance
5,08239 gold badges39 silver badges58 bronze badges
answered Mar 24, 2022 at 16:29
2

Anyone who wants to do a deep copy (e.g. if your array contains objects) can use:

let arrCopy = JSON.parse(JSON.stringify(arr))

Then you can sort arrCopy without changing arr.

arrCopy.sort((obj1, obj2) => obj1.id > obj2.id)

Please note: this can be slow for very large arrays.

answered Jan 21, 2020 at 10:26
2
  • 1
    This will work with - instead of > in your second example. Commented May 15, 2020 at 16:57
  • 2
    and remember all your items should be serializable in order to bring them back after stringifying ( eg. date objects, functions and symbols are problematic in this method ) Commented Jan 20, 2021 at 13:47
2

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

For example:

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

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.

answered Apr 27, 2022 at 10:54
0

You can also extend the existing Array functionality. This allows chaining different array functions together.

Array.prototype.sorted = function (compareFn) {
 const shallowCopy = this.slice();
 shallowCopy.sort(compareFn);
 return shallowCopy;
}
[1, 2, 3, 4, 5, 6]
 .filter(x => x % 2 == 0)
 .sorted((l, r) => r - l)
 .map(x => x * 2)
// -> [12, 8, 4]

Same in typescript:

// extensions.ts
Array.prototype.sorted = function (compareFn?: ((a: any, b: any) => number) | undefined) {
 const shallowCopy = this.slice();
 shallowCopy.sort(compareFn);
 return shallowCopy;
}
declare global {
 interface Array<T> {
 sorted(compareFn?: (a: T, b: T) => number): Array<T>;
 }
}
export {}
// index.ts
import 'extensions.ts';
[1, 2, 3, 4, 5, 6]
 .filter(x => x % 2 == 0)
 .sorted((l, r) => r - l)
 .map(x => x * 2)
// -> [12, 8, 4]
answered Dec 13, 2022 at 12:29
0

To sort a function without mutating the original, simply use .map() before sort to create a copy of the original array:

const originalArr = [1, 45, 3, 21, 6];
const sortedArr = originalArr.map(value => JSON.parse(JSON.stringify(value))).sort((a, b) => a - b);
console.log(sortedArr); // the logged output will be 1,3,6,21,45

The original array has not been modified, but you have a sorted version of it available to you. JSON.parse(JSON.stringify()) make sure it is a deep copy, not a shallow copy.

Mr. Polywhirl
49.1k12 gold badges95 silver badges147 bronze badges
answered Mar 30, 2023 at 9:31

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.