ChatGPT解决这个技术问题 Extra ChatGPT

How can you sort an array without mutating the original array?

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?

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

P
Putzi San

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


this is really great.i think easier to understand than the concat and other approaches
this code does work, but when I compile the JS with Gulp it comes back with an error, SyntaxError: Unexpected token: punc (.)
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.
@Cerin It sounds like you are on an incredibly outdated version of JS.
Is this method faster than .slice() to get a copy of the array?
R
Rob W

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

Will this do a deep copy, i.e., will nested objects and arrays also be copied?
Is there any advantage to using concat over say slice(0) or are they all pretty much just the same?
@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.
Slice is now reported as notably faster
why Array.prototype.slice.call(arr).sort(); instead of arr.slice().sort(); ?
J
JaredPar

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.


z
zzzzBov

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

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

This answer is awesome! I'm surprised JavaScript allows mutation to this degree. Seems wrong. Thanks, again.
A
Aditya Agarwal

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

This answer is nice
H
Hamada

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.


This will work with - instead of > in your second example.
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 )
R
Ran Turner

Update - Array.prototype.toSorted() proposal

The Array.prototype.toSorted(compareFn) -> Array is a method which 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 returns a copy of it with the change performed instead.


P
Prati

Try this to sort the numbers. This does not mutate the original array.

function sort(arr) {
  return arr.slice(0).sort((a,b) => a-b);
}

J
Josh

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.


O
Oleh Dzoba

I think that my answer is a bit too late but if someone come across this issue again the solution may be useful.

I can propose yet another approach with a native function which returns a sorted array.

This code still mutates the original object but instead of native behaviour this implementation returns a sorted array.

// Remember that it is not recommended to extend build-in prototypes 
// or even worse override native functions.  
// You can create a seperate function if you like

// You can specify any name instead of "sorted" (Python-like)

// Check for existence of the method in prototype
if (typeof Array.prototype.sorted == "undefined") {
  // If it does not exist you provide your own method
  Array.prototype.sorted = function () {
    Array.prototype.sort.apply(this, arguments);
    return this;
  };
}

This way of solving the problem was ideal in my situation.