Jak posortować tablicę bez mutowania oryginalnej tablicy?

230

Załóżmy, że chciałem funkcji sortowania, która zwraca posortowaną kopię wprowadzonej tablicy. Naiwnie tego próbowałem

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

i przetestowałem to z tym, co pokazuje, że moja sortmetoda mutuje tablicę.

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"

Próbowałem również tego podejścia

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

ale to w ogóle nie działa.

Czy istnieje prosty sposób obejścia tego problemu, najlepiej taki, który nie wymaga ręcznego zwijania własnego algorytmu sortowania lub kopiowania każdego elementu tablicy do nowego?

Peter Olson
źródło
1
utwórz głęboką kopię tablicy i posortuj ją zamiast tego.
evanmcdonnal
1
@evanmcdonnal Płytka kopia może być wystarczająca, jeśli wszystko, czego potrzeba, to zmiana kolejności, a nie duplikat każdego elementu w tablicy.
Kekoa,
.sortwymaga, aby thiswartością była tablica, więc aby ostatni fragment działał, zrobiłbyś to .sort.call(arr)(choć nie rozwiązuje to problemu).
pimvdb,
@Kekoa Tak, to dobra uwaga. Nie ma potrzeby zużywać więcej pamięci, jeśli zamierzasz jedynie zmieniać kolejność elementów, a nie same elementy.
evanmcdonnal
Metoda zzzzBov działa jak urok! stackoverflow.com/a/9592774/7011860
Samet M.

Odpowiedzi:

222

Po prostu skopiuj tablicę. Można to zrobić na wiele sposobów:

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

// Or:
return Array.prototype.slice.call(arr).sort(); // For array-like objects
Rob W.
źródło
2
Czy spowoduje to głęboką kopię, tj. Czy zagnieżdżone obiekty i tablice również zostaną skopiowane?
Peter Olson,
2
Czy jest jakaś przewaga w korzystaniu concatz powiedzeń, slice(0)czy są one w zasadzie takie same?
JaredPar,
3
@PeterOlson Nie, to płytka kopia. Jeśli naprawdę potrzebujesz głębokiej kopii, skorzystaj z funkcji wyszukiwania w przepełnieniu stosu, aby znaleźć na to doskonałe doskonałe odpowiedzi.
Rob W
11
Plasterek jest teraz zgłaszany jako znacznie szybszy
Zander Brown
3
dlaczego Array.prototype.slice.call(arr).sort();zamiast arr.slice().sort();?
Olivier Boissé
61

Spróbuj wykonać następujące czynności

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

slice(0)Ekspresji tworzy kopię początkowego tablicy w elemencie 0.

JaredPar
źródło
32

Możesz użyć wycinka bez argumentów, aby skopiować tablicę:

var foo,
    bar;
foo = [3,1,2];
bar = foo.slice().sort();
zzzzBov
źródło
Ta odpowiedź jest niesamowita! Dziwi mnie, że JavaScript umożliwia mutację do tego stopnia. Wydaje się źle. Dzięki jeszcze raz.
12

Możesz także to zrobić

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

W ten sposób d nie zostanie zmutowany.

function sorted(arr) {
  temp = Array.from(arr)
  return temp.sort()
}

//Use it like this
x = [20, 10, 100]
console.log(sorted(x))
Aditya Agarwal
źródło
Ta odpowiedź jest ładna
Leasye
1

Każdy, kto chce wykonać głęboką kopię (np. Jeśli tablica zawiera obiekty), może użyć:

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

Następnie możesz sortować arrCopybez zmian arr.

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

Uwaga: może to być powolne w przypadku bardzo dużych tablic.

Hamada
źródło
Będzie to działać -zamiast >w drugim przykładzie.
pootzko
0

Używam Object.assign () do większości moich kopii:

var copyArray = Object.assign([], originalArray).sort();

Jednak po przejrzeniu komentarzy OP, zbadałem trochę głębokiego kopiowania i okazało się, że Object.assign nie tylko wykonuje płytką kopię, ale także wybiera tylko wyliczalne i własne właściwości (zgodnie z odpowiedzią w tym poście ).

Sun Lee
źródło