Sort JavaScript : pourquoi 10 passe avant 2 et comment trier sans muter vos tableaux

Sort JavaScript : pourquoi 10 passe avant 2 et comment trier sans muter vos tableaux

La méthode sort() permet d’ordonner un tableau JavaScript, mais son comportement par défaut réserve un piège classique. Sans fonction de comparaison, elle traite les valeurs comme des chaînes : 10 peut donc passer avant 2. Voici comment trier correctement des nombres, du texte et des objets, tout en évitant de modifier involontairement vos données.

Ce que fait réellement sort() sur un tableau JavaScript

La syntaxe de base est tableau.sort(). Cette méthode effectue un tri en place : elle réorganise le tableau existant, puis renvoie une référence vers ce même tableau. Elle ne crée pas une nouvelle liste.

Avec const fruits = ["Banana", "Orange", "Apple", "Mango"];, l’instruction fruits.sort(); produit ["Apple", "Banana", "Mango", "Orange"]. Pour des chaînes simples, le résultat semble naturel. En réalité, le moteur compare les séquences d’unités de code UTF-16, et non un ordre alphabétique adapté à toutes les langues.

Le piège apparaît avec const valeurs = [1, 2, 10, 21];. Après valeurs.sort();, le tableau devient [1, 10, 2, 21]. JavaScript convertit implicitement les nombres en chaînes, puis compare notamment leur premier caractère. Comme « 1 » précède « 2 », 10 est placé avant 2.

Un retour qui masque la mutation

Cette écriture est valide : const trie = valeurs.sort();. Toutefois, trie et valeurs désignent le même tableau en mémoire. Modifier l’un affecte donc l’autre. Ce comportement compte dans une application où l’ordre initial est réutilisé, par exemple pour l’état d’une interface, une liste filtrée ou des données reçues d’une API.

La méthode reverse() suit la même logique : elle inverse le tableau original en place. Un appel à valeurs.sort().reverse() permet d’inverser un ordre déjà établi, mais il enchaîne deux mutations. Si le tableau doit rester disponible dans son état initial, mieux vaut créer une copie ou utiliser une méthode non mutante.

Le comparateur qui remet les nombres dans le bon ordre

Pour un tri numérique fiable, fournissez une fonction de comparaison. En ordre croissant, utilisez nombres.sort((a, b) => a - b);. Avec const nombres = [40, 100, 1, 5, 25, 10];, le résultat est [1, 5, 10, 25, 40, 100].

Pour l’ordre décroissant, inversez la soustraction : nombres.sort((a, b) => b - a);. Le résultat devient [100, 40, 25, 10, 5, 1]. Cette forme est plus explicite et plus sûre qu’un simple reverse() lorsque le tableau n’a pas déjà été trié numériquement.

Lire la fonction de comparaison sans la mémoriser

Le comparateur reçoit deux éléments, a et b. S’il renvoie une valeur négative, a est placé avant b. S’il renvoie une valeur positive, b passe avant a. Avec zéro, les deux éléments sont équivalents pour le critère choisi. Ainsi, pour 40 et 100, 40 - 100 = -60 : 40 doit précéder 100.

Le comparateur ne donne pas directement une position finale. Il indique l’ordre relatif entre deux éléments lorsque le moteur les compare. Pour obtenir un résultat fiable, la règle doit rester cohérente et utiliser le même critère pour toutes les paires. Un comparateur contradictoire peut produire un classement inattendu.

Évitez les comparateurs imprécis sur des données mixtes. Si un tableau contient des nombres, NaN, des chaînes ou null, définissez d’abord une règle précise : exclure les valeurs invalides, les placer à la fin ou les convertir explicitement. Un comparateur numérique ne rend pas automatiquement fiables des données hétérogènes.

Conserver le tableau original avec toSorted() ou une copie

Lorsque l’ordre de départ doit rester disponible, toSorted() est l’alternative moderne à sort(). Introduite avec ES2023, elle retourne un nouveau tableau trié sans modifier celui d’origine. Par exemple, const classement = scores.toSorted((a, b) => b - a); conserve scores intact.

Si votre environnement ne propose pas encore cette méthode, copiez le tableau avant le tri : const classement = [...scores].sort((a, b) => b - a);. La syntaxe spread crée un nouveau tableau, mais les objets qu’il contient restent les mêmes références. Modifier un objet dans la copie peut donc aussi modifier l’objet présent dans le tableau initial.

Méthode Modifie le tableau initial Résultat Usage principal
sort() Oui Le même tableau, trié Trier en place
toSorted() Non Un nouveau tableau trié Préserver les données initiales
reverse() Oui Le même tableau, inversé Renverser l’ordre actuel
toReversed() Non Un nouveau tableau inversé Inverser sans mutation
TypedArray.prototype.sort() Oui Le même tableau typé, trié Trier des données numériques typées

toReversed(), également introduite avec ES2023, est à reverse() ce que toSorted() est à sort() : une variante non mutante. Pour vérifier la syntaxe et les particularités selon votre environnement, consultez la documentation de référence de Array.prototype.sort() sur MDN.

Trier des textes et des objets selon un vrai critère métier

Accents, casse et ordre alphabétique

Pour des noms, des villes ou des libellés en français, préférez localeCompare() au tri par défaut. L’expression villes.sort((a, b) => a.localeCompare(b, "fr")); applique une comparaison linguistique mieux adaptée aux accents et aux conventions locales. Pour l’ordre décroissant, inversez les termes : villes.sort((a, b) => b.localeCompare(a, "fr"));.

Le tri par défaut reste pertinent pour des identifiants techniques lorsque l’ordre UTF-16 recherché est volontaire. Pour du texte visible par des utilisateurs, localeCompare() exprime mieux l’intention et rend le code plus clair. Les options de comparaison peuvent aussi préciser le traitement de la casse lorsque le besoin le justifie.

Classer un tableau d’objets

Pour trier des objets, le comparateur cible une propriété. Avec const produits = [{ nom: "Clavier", prix: 89 }, { nom: "Souris", prix: 35 }];, utilisez produits.sort((a, b) => a.prix - b.prix); pour obtenir un ordre croissant des prix. Pour les noms, écrivez produits.sort((a, b) => a.nom.localeCompare(b.nom, "fr"));.

Le tri JavaScript est stable depuis ES2019 : lorsque le comparateur renvoie zéro, les éléments conservent leur ordre relatif initial. Cette propriété est utile pour des tris successifs. Vous pouvez, par exemple, ordonner des produits par nom, puis appliquer un tri par catégorie. Les produits qui partagent une catégorie conservent alors l’ordre de nom déjà établi.

Cas particuliers : tableaux typés, performances et mélange aléatoire

Un TypedArray, tel que Int32Array ou Float64Array, dispose aussi de sort(). Sa différence principale avec Array.prototype.sort() concerne le comportement par défaut : il trie numériquement. Ainsi, new Int32Array([1, 10, 2]).sort() donne bien 1, 2, 10, sans comparateur.

La spécification JavaScript ne garantit ni un algorithme de tri ni une complexité temporelle ou spatiale identique d’un moteur à l’autre. Pour des listes courantes, privilégiez donc la clarté du comparateur. Sur de grands volumes, évitez aussi de trier uniquement pour obtenir une valeur extrême : Math.min(...nombres), Math.max(...nombres) ou une boucle dédiée répondent plus directement à ce besoin.

Enfin, ne mélangez pas un tableau avec sort(() => 0.5 - Math.random()). Ce comparateur aléatoire produit une distribution biaisée et dépend du comportement du tri. L’algorithme de Fisher-Yates est la méthode appropriée : il parcourt le tableau et échange chaque position avec une position aléatoire parmi celles qui restent. Trier et mélanger sont deux opérations différentes ; les confondre peut introduire un biais dans un jeu, un tirage ou un test.

À découvrir ensuite