Comment trier un tableau sans muter le tableau d'origine?


230

Supposons que je voulais une fonction de tri qui renvoie une copie triée du tableau entré. J'ai essayé naïvement

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

et je l'ai testé avec cela, ce qui montre que ma sortméthode mute le tableau.

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"

J'ai aussi essayé cette approche

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

mais ça ne marche pas du tout.

Y a-t-il un moyen simple de contourner cela, de préférence un moyen qui ne nécessite pas de rouler à la main mon propre algorithme de tri ou de copier chaque élément du tableau dans un nouvel?


1
créez une copie complète du tableau et triez-la à la place.
evanmcdonnal

1
@evanmcdonnal Une copie superficielle peut être suffisante si tout ce que vous voulez est une réorganisation et non un doublon de chaque élément du tableau.
Kekoa

.sortrequiert que la thisvaleur soit le tableau, donc pour que le dernier extrait fonctionne, vous feriez .sort.call(arr)(bien que cela ne résout pas votre problème).
pimvdb

@Kekoa Oui, c'est un bon point. Il n'est pas nécessaire de consommer plus de mémoire si vous allez seulement changer l'ordre des éléments et non les éléments eux-mêmes.
evanmcdonnal

La méthode de zzzzBov fonctionne comme un charme! stackoverflow.com/a/9592774/7011860
Samet M.

Réponses:


223

Copiez simplement le tableau. Il y a plusieurs façons de procéder:

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

// Or:
return Array.prototype.slice.call(arr).sort(); // For array-like objects

2
Cela fera-t-il une copie complète, c'est-à-dire que les objets et les tableaux imbriqués seront également copiés?
Peter Olson

2
Y a-t-il un avantage à utiliser concatover say slice(0)ou sont-ils tous à peu près les mêmes?
JaredPar

3
@PeterOlson Non, c'est une copie superficielle. Si vous voulez vraiment une copie complète, utilisez la fonction de recherche sur Stack Overflow pour trouver d'excellentes réponses existantes pour cela.
Rob W

11
La tranche est désormais signalée comme étant beaucoup plus rapide
Zander Brown

3
pourquoi Array.prototype.slice.call(arr).sort();au lieu de arr.slice().sort();?
Olivier Boissé


61

Essayez ce qui suit

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

L' slice(0)expression crée une copie du tableau à partir de l'élément 0.


32

Vous pouvez utiliser une tranche sans argument pour copier un tableau:

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

Cette réponse est géniale! Je suis surpris que JavaScript autorise la mutation à ce degré. Semble mal. Merci encore.

12

Vous pouvez aussi faire ça

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

De cette façon, d ne sera pas muté.

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

//Use it like this
x = [20, 10, 100]
console.log(sorted(x))

Cette réponse est agréable
Leasye

1

Quiconque veut faire une copie complète (par exemple si votre tableau contient des objets) peut utiliser:

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

Ensuite, vous pouvez trier arrCopysans changer arr.

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

Remarque: cela peut être lent pour les très grands tableaux.


Cela fonctionnera avec -au lieu de >dans votre deuxième exemple.
pootzko

0

J'utilise Object.assign () pour la plupart de mes copies:

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

Cependant, après avoir parcouru les commentaires OP, j'ai recherché un peu de copie en profondeur et il s'avère qu'Object.assign effectue non seulement une copie superficielle, mais sélectionne également uniquement les propriétés énumérables et propres (comme répondu dans ce post ).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.