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?


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

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.


32

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

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

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

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.


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.