Как вы можете отсортировать массив без изменения исходного массива?

230

Предположим, мне нужна функция сортировки, которая возвращает отсортированную копию введенного массива. Я наивно пробовал это

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

и я проверил это с этим, что показывает, что мой sortметод мутирует массив.

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"

Я тоже попробовал этот подход

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

но это не работает вообще.

Есть ли прямой способ обойти это, предпочтительно способ, который не требует ручного скручивания моего собственного алгоритма сортировки или копирования каждого элемента массива в новый?

Питер Олсон
источник
1
создайте глубокую копию массива и отсортируйте его.
evanmcdonnal
1
@evanmcdonnal Мелкая копия может быть достаточно хорошей, если все, что требуется, - это переупорядочение, а не дубликат каждого элемента в массиве.
Кекоа
.sortтребует, чтобы thisзначение было массивом, поэтому, чтобы последний фрагмент работал, вы бы это сделали .sort.call(arr)(хотя это не решит вашу проблему).
pimvdb
@Kekoa Да, это хороший момент. Нет необходимости использовать больше памяти, если вы собираетесь изменить только порядок элементов, а не сами элементы.
evanmcdonnal
Метод zzzzBov работает как шарм! stackoverflow.com/a/9592774/7011860
Самет М.

Ответы:

223

Просто скопируйте массив. Есть много способов сделать это:

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

// Or:
return Array.prototype.slice.call(arr).sort(); // For array-like objects
Роб W
источник
2
Будет ли это делать глубокое копирование, т. Е. Будут ли скопированы вложенные объекты и массивы?
Питер Олсон
2
Есть ли какое-то преимущество в использовании concatover say slice(0)или все они почти одинаковые?
JaredPar
3
@PeterOlson Нет, это мелкая копия. Если вам действительно нужна глубокая копия, используйте функцию поиска в переполнении стека, чтобы найти существующие отличные ответы для этого.
Роб У
11
Срез теперь известен как заметно более быстрый
Зандер Браун
3
почему Array.prototype.slice.call(arr).sort();вместо arr.slice().sort();?
Оливье Буасе
61

Попробуйте следующее

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

slice(0)Выражение создает копию массива , начиная с элемента 0.

JaredPar
источник
32

Вы можете использовать слайс без аргументов для копирования массива:

var foo,
    bar;
foo = [3,1,2];
bar = foo.slice().sort();
zzzzBov
источник
Этот ответ потрясающий! Я удивлен, что JavaScript допускает мутации до такой степени. Кажется неправильным Еще раз спасибо.
12

Вы также можете сделать это

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

Таким образом, d не будет видоизменяться.

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

//Use it like this
x = [20, 10, 100]
console.log(sorted(x))
Адитья Агарвал
источник
Этот ответ хорош
Leasye
1

Любой, кто хочет сделать глубокую копию (например, если ваш массив содержит объекты), может использовать:

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

Тогда вы можете сортировать arrCopyбез изменений arr.

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

Обратите внимание: это может быть медленно для очень больших массивов.

Hamada
источник
Это будет работать -вместо >вашего второго примера.
pootzko
0

Я использую Object.assign () для большинства моих копий:

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

Однако, просмотрев комментарии OP, я немного углубился в копирование и выяснил, что Object.assign не только выполняет поверхностное копирование, но и только выбирает перечисляемые и собственные свойства (как ответили в этом посте ).

Сан Ли
источник