132 votes

Comment fonctionne la fonction sort() de Javascript ?

Comment le code suivant permet-il de trier ce tableau par ordre numérique ?

var array=[25, 8, 7, 41]

array.sort(function(a,b){
  return a - b
})

Je sais que si le résultat du calcul est...

Inférieur à 0 : "a" est trié pour être un indice inférieur à "b".
Zéro : "a" et "b" sont considérés comme égaux et aucun tri n'est effectué.
Supérieure à 0 : "b" est trié pour être un indice inférieur à "a".

La fonction de rappel du tri de tableaux est-elle appelée plusieurs fois au cours du tri ?

Si c'est le cas, j'aimerais savoir quels sont les deux nombres qui sont transmis à la fonction à chaque fois. J'ai supposé qu'elle prenait d'abord "25"(a) et "8"(b), puis "7"(a) et "41"(b), donc.. :

25(a) - 8(b) = 17 (supérieur à zéro, donc trier "b" pour qu'il soit un indice inférieur à "a") : 8, 25

7(a) - 41(b) = -34 (inférieur à zéro, donc trier "a" pour qu'il soit un indice inférieur à "b" : 7, 41

Comment les deux ensembles de nombres sont-ils ensuite triés l'un par rapport à l'autre ?

Aidez un débutant qui a du mal à s'y retrouver !

68voto

OscarRyz Points 82553

La fonction de rappel du tri de tableaux est-elle appelée plusieurs fois au cours du tri ?

Oui

Si c'est le cas, j'aimerais savoir quels sont les deux nombres qui sont transmis à la fonction à chaque fois

Vous pouvez le découvrir vous-même avec :

array.sort((a,b) => {
  console.log(`comparing ${a},${b}`);
  return a > b ? 1
               : a === b ? 0 
                         : -1;
});

EDIT

Voici le résultat que j'ai obtenu :

25,8
25,7
8,7
25,41

53voto

Warren Young Points 16324

L'interpréteur JavaScript dispose d'une sorte de algorithme de tri intégrée. Il appelle la fonction de comparaison un certain nombre de fois au cours de l'opération de tri. Le nombre de fois où la fonction de comparaison est appelée dépend de l'algorithme particulier, des données à trier et de l'ordre dans lequel elles se trouvent avant le tri.

Certains algorithmes de tri sont peu performants sur des listes déjà triées, car ils doivent effectuer beaucoup plus de comparaisons que dans le cas habituel. D'autres s'accommodent bien des listes pré-triées, mais il existe d'autres cas où ils peuvent être "trompés" pour obtenir des résultats médiocres.

De nombreux algorithmes de tri sont couramment utilisés, car aucun n'est parfait pour tous les usages. Les deux algorithmes les plus utilisés pour les tris génériques sont Tri sélectif y fusionner trier . Le tri rapide est souvent le plus rapide des deux, mais le tri par fusion possède des propriétés intéressantes qui peuvent en faire un meilleur choix global. Le tri par fusion est stable alors que Quicksort ne l'est pas. Les deux algorithmes peuvent être parallélisés, mais la façon dont fonctionne le tri par fusion rend une implémentation parallèle plus efficace, toutes choses étant égales par ailleurs.

Votre interprète JavaScript peut utiliser l'un de ces algorithmes ou quelque chose d'autre. L'interpréteur ECMAScript standard ne précise pas quel algorithme qu'une implémentation conforme doit utiliser. Il désavoue même explicitement le besoin de stabilité.

13voto

Nosredna Points 33670

Des paires de valeurs sont comparées, une paire à la fois. Les paires qui sont comparées sont un détail d'implémentation - ne supposez pas qu'elles seront les mêmes sur tous les navigateurs. Le rappel peut être n'importe quoi (vous pouvez donc trier des chaînes de caractères ou des chiffres romains ou n'importe quoi d'autre pour lequel vous pouvez trouver une fonction qui renvoie 1,0,-1).

Il convient de garder à l'esprit que la stabilité du tri de JavaScript n'est pas garantie.

12voto

Aman Silawat Points 31

Connaissance approfondie

Si le résultat est négatif, a est trié avant b.

Si le résultat est positif, b est trié avant a.

Si le résultat est 0, aucune modification n'est apportée à l'ordre de tri des deux valeurs.

NOTE :

Ce code est la vue à l'intérieur du trier méthode étape par étape.

SORTIE :

let arr = [90, 1, 20, 14, 3, 55];
var sortRes = [];
var copy = arr.slice();     //create duplicate array
var inc = 0;    //inc meant increment
copy.sort((a, b) => {
    sortRes[inc] = [ a, b, a-b ];
    inc += 1;
    return a - b;
});
var p = 0;
for (var i = 0; i < inc; i++) {
    copy = arr.slice();
    copy.sort((a, b) => {
        p += 1;
        if (p <= i ) {
            return a - b;
        }
        else{
            return false;
        }
    });
    p = 0;
    console.log(copy +' \t a: '+ sortRes[i][0] +' \tb: '+ sortRes[i][1] +'\tTotal: '+ sortRes[i][2]);
}

9voto

ggorlen Points 5267

Pour aider à clarifier le comportement des Array#sort et son comparateur, considérez ce naïf tri par insertion enseignés dans les cours de programmation débutants :

const sort = arr => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && arr[j-1] > arr[j]; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array);
console.log("" + array);

Sans tenir compte du choix de l'algorithme de tri par insertion, il convient de se concentrer sur l'algorithme de tri par insertion. codé en dur comparateur : arr[j-1] > arr[j] . Cela pose deux problèmes pertinents pour la discussion :

  1. Les > est invoqué sur des paires d'éléments de tableau, mais de nombreux éléments que vous pourriez vouloir trier, tels que les objets, ne répondent pas à l'opérateur > de manière raisonnable (il en irait de même si l'on utilisait des - ).
  2. Même si vous travaillez avec des nombres, vous avez souvent besoin d'un autre arrangement que le tri ascendant qui a été intégré ici.

Nous pouvons résoudre ces problèmes en ajoutant un comparefn que vous connaissez bien :

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

const array = [3, 0, 4, 5, 2, 2, 2, 1, 2, 2, 0];
sort(array, (a, b) => a - b);
console.log("" + array);

sort(array, (a, b) => b - a);
console.log("" + array);

const objArray = [{id: "c"}, {id: "a"}, {id: "d"}, {id: "b"}];
sort(objArray, (a, b) => a.id.localeCompare(b.id));
console.log(JSON.stringify(objArray, null, 2));

La routine de tri naïf est maintenant généralisée. Vous pouvez voir exactement quand ce rappel est invoqué, ce qui répond à votre première préoccupation :

La fonction de rappel du tri de tableaux est-elle appelée plusieurs fois au cours du tri ? Si c'est le cas, j'aimerais savoir quels sont les deux nombres qui sont transmis à la fonction à chaque fois

L'exécution du code ci-dessous montre que, oui, la fonction est appelée plusieurs fois et que vous pouvez utiliser console.log pour voir quels numéros ont été transmis :

const sort = (arr, comparefn) => {
  for (let i = 1; i < arr.length; i++) {
    for (let j = i; j && comparefn(arr[j-1], arr[j]) > 0; j--) {
      [arr[j], arr[j-1]] = [arr[j-1], arr[j]];
    }
  }
};

console.log("on our version:");
const array = [3, 0, 4, 5];
sort(array, (a, b) => console.log(a, b) || (a - b));
console.log("" + array);

console.log("on the builtin:");
console.log("" + 
  [3, 0, 4, 5].sort((a, b) => console.log(a, b) || (a - b))
);

Vous demandez :

Comment les deux ensembles de nombres sont-ils ensuite triés l'un par rapport à l'autre ?

Pour être précis dans la terminologie, a y b ne sont pas ensembles de nombres - ce sont des objets dans le tableau (dans votre exemple, ce sont des nombres).

La vérité est que cela n'a pas d'importance comment ils sont triés parce qu'ils dépendent de la mise en œuvre. Si j'avais utilisé un algorithme de tri différent du tri par insertion, le comparateur serait probablement invoqué sur différentes paires de nombres, mais à la fin de l'appel au tri, l'invariant qui importe au programmeur JS est que le tableau de résultats est trié selon le comparateur, en supposant que le comparateur renvoie des valeurs qui adhèrent au contrat que vous avez énoncé (< 0 quand a < b , 0 lorsque a === b et > 0 lorsque a > b ).

De même que je suis libre de modifier l'implémentation de mon tri tant que je n'enfreins pas ma spécification, les implémentations d'ECMAScript sont libres de choisir l'implémentation du tri dans les limites de la norme spécification de la langue Ainsi Array#sort produira probablement des appels de comparateurs différents sur des moteurs différents. On n'écrirait pas un code dont la logique repose sur une séquence particulière de comparaisons (et le comparateur ne devrait pas non plus produire d'effets secondaires).

Par exemple, le moteur V8 (au moment de la rédaction) invoque Timsort lorsque le tableau est plus grand qu'un certain nombre d'éléments calculés à l'avance et utilise une fonction tri par insertion binaire pour les petits morceaux de tableau. Cependant, il a l'habitude d'utiliser tri sélectif qui est instable et qui donnerait probablement une séquence différente d'arguments et d'appels au comparateur.

Étant donné que les différentes implémentations de tri utilisent différemment la valeur de retour de la fonction de comparaison, cela peut conduire à un comportement surprenant lorsque le comparateur ne respecte pas le contrat. Voir ce fil à titre d'exemple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X