327
votes

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, prefereably a way that doesn't require hand-rolling my own sorting algorithm or copying every element of the array into a new one?

6
create a deep copy of the array and sort it instead. - evanmcdonnal
@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. - Kekoa
.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). - pimvdb
@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. - evanmcdonnal
zzzzBov's method is working like a charm! stackoverflow.com/a/9592774/7011860 - Samet M.

6 Answers

296
votes

You need to copy the array before you sort it. One way with es6:

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

the spread-syntax as array literal (copied from mdn):

var arr = [1, 2, 3];
var arr2 = [...arr]; // like arr.slice()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

244
votes

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
75
votes

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.

43
votes

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

var foo,
    bar;
foo = [3,1,2];
bar = foo.slice().sort();
17
votes

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))
3
votes

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.