Comment effectuer un tri non sensible à la casse en JavaScript?

220

J'ai un tableau de chaînes que je dois trier en JavaScript, mais de manière non sensible à la casse. Comment faire ça?

Jérôme Verstrynge
la source

Réponses:

404

Dans (presque :) un one-liner

["Foo", "bar"].sort(function (a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
});

Ce qui se traduit par

[ 'bar', 'Foo' ]

Tandis que

["Foo", "bar"].sort();

résulte en

[ 'Foo', 'bar' ]
Ivan Krechetov
la source
9
Gardez à l'esprit que les options avancées de localeCompare ne sont pas encore prises en charge sur toutes les plateformes / navigateurs. Je sais qu'ils ne sont pas utilisés dans cet exemple, mais je voulais juste ajouter pour plus de clarté. Voir MDN pour plus d'informations
Ayame__
97
Si vous allez impliquer localeCompare (), vous pouvez simplement utiliser sa capacité à ne pas respecter la casse, par exemple:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Michael Dyck
2
+1 pour ne pas appeler toLowerCase()alors localeCompareque cela se fait déjà par défaut dans certains cas. Vous pouvez en savoir plus sur les paramètres à lui transmettre ici: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Milimetric
3
@ Accord milimétrique à la page référencée, cette fonctionnalité n'est pas prise en charge par certains navigateurs (par exemple IE <11 ou Safari). la solution mentionnée ici est très bonne, mais nécessiterait tout de même un rétroportage / polyfill pour certains navigateurs.
3
2
Si vous avez un grand tableau, il est judicieux de l'utiliser items.sort(new Intl.Collator('en').compare)pour de meilleures performances. (Voir MDN .)
valtlai
60
myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

EDIT: Veuillez noter que j'ai écrit à l'origine pour illustrer la technique plutôt que d'avoir à l'esprit la performance. Veuillez également vous référer à la réponse @Ivan Krechetov pour une solution plus compacte.

ron tornambe
la source
3
Cela peut appeler toLowerCasedeux fois sur chaque chaîne; serait plus efficace pour stocker des versions réduites de la chaîne dans des variables.
Jacob
Vrai et merci. J'ai écrit cela dans un esprit de clarté, pas de performance. Je suppose que je devrais le noter.
ron tornambe du
1
@Jacob Pour être honnête, la réponse acceptée a le même problème de base: elle peut éventuellement appeler .toLowerCase()plusieurs fois pour chaque élément du tableau. Par exemple, 45 appels à la fonction de comparaison lors du tri de 10 éléments dans l'ordre inverse. var i = 0; ["z","y","x","w","v","u","t","s","r","q"].sort(function (a, b) {++i; return a.toLowerCase().localeCompare(b.toLowerCase());}); console.log("Calls to Compare: " + i); // i === 45
rien n'est nécessaire
47

Il est temps de revenir sur cette vieille question.

Vous ne devez pas utiliser de solutions sur lesquelles vous vous fondez toLowerCase. Ils sont inefficaces et ne fonctionnent tout simplement pas dans certaines langues (turc par exemple). Préférez ceci:

['Foo', 'bar'].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))

Consultez la documentation pour la compatibilité du navigateur et tout ce qu'il y a à savoir sur l' sensitivityoption.

ZunTzu
la source
1
Attention, ceci n'est pas pris en charge dans tous les moteurs javascript.
Luboš Turek
26
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if (a == b) return 0;
    if (a > b) return 1;
    return -1;
});
Niet l'Absolu Noir
la source
1
ou return a === b ? 0 : a > b ? 1 : -1;
Devin G Rhode
Cela ne fonctionnera probablement pas comme prévu pour les chaînes qui représentent des nombres. Les opérateurs arithmétiques utiliseront la sémantique des nombres au lieu des chaînes. Par exemple, si nous l'avons ["111", "33"], nous pourrions souhaiter qu'il revienne ["111", "33"]car 1 précède 3 dans l'ordre des codes de caractères. Cependant, la fonction dans cette réponse retournera ["33", "111"]car le nombre 33est inférieur au nombre 111.
Austin Davis
@AustinDavis "33" > "111" === trueet 33 > 111 === false. Cela fonctionne comme prévu.
Niet the Dark Absol
12

Vous pouvez également utiliser le nouveau Intl.Collator().compare, par MDN, il est plus efficace lors du tri des tableaux. L'inconvénient est qu'il n'est pas pris en charge par les anciens navigateurs. MDN déclare qu'il n'est pas du tout pris en charge dans Safari. Besoin de le vérifier, car il indique que cela Intl.Collatorest pris en charge.

Lors de la comparaison d'un grand nombre de chaînes, comme dans le tri de grands tableaux, il est préférable de créer un objet Intl.Collator et d'utiliser la fonction fournie par sa propriété compare

["Foo", "bar"].sort(Intl.Collator().compare); //["bar", "Foo"]
mateuscb
la source
11

Si vous souhaitez garantir le même ordre quel que soit l'ordre des éléments dans le tableau d'entrée, voici un tri stable :

myArray.sort(function(a, b) {
    /* Storing case insensitive comparison */
    var comparison = a.toLowerCase().localeCompare(b.toLowerCase());
    /* If strings are equal in case insensitive comparison */
    if (comparison === 0) {
        /* Return case sensitive comparison instead */
        return a.localeCompare(b);
    }
    /* Otherwise return result */
    return comparison;
});
Aalex Gabi
la source
5

Normaliser le cas dans le .sort()avec .toLowerCase().


la source
4

Vous pouvez également utiliser l'opérateur Elvis:

arr = ['Bob', 'charley', 'fudge', 'Fudge', 'biscuit'];
arr.sort(function(s1, s2){
    var l=s1.toLowerCase(), m=s2.toLowerCase();
    return l===m?0:l>m?1:-1;
});
console.log(arr);

Donne:

biscuit,Bob,charley,fudge,Fudge

La méthode localeCompare est probablement très bien cependant ...

Remarque: L'opérateur Elvis est un «opérateur ternaire» abrégé pour if if else, généralement avec affectation.
Si vous regardez le?: De côté, il ressemble à Elvis ...
c'est-à-dire au lieu de:

if (y) {
  x = 1;
} else {
  x = 2;
}

vous pouvez utiliser:

x = y?1:2;

c'est-à-dire quand y est vrai, alors retourne 1 (pour l'affectation à x), sinon retourne 2 (pour l'affectation à x).

AndyS
la source
5
Pour être pédant, ce n'est pas l'opérateur Elvis. Ceci est juste un opérateur ternaire de base. Un véritable opérateur Elvis est coalescent nul, par exemple, au lieu de x = y ? y : z, vous pouvez le faire x = y ?: z. Javascript n'a pas d'opérateur Elvis réel, mais vous pouvez l'utiliser x = y || zde manière similaire.
Charles Wood
3

Les autres réponses supposent que le tableau contient des chaînes. Ma méthode est meilleure, car elle fonctionnera même si le tableau contient null, undefined ou d'autres non-chaînes.

var notdefined;
var myarray = ['a', 'c', null, notdefined, 'nulk', 'BYE', 'nulm'];

myarray.sort(ignoreCase);

alert(JSON.stringify(myarray));    // show the result

function ignoreCase(a,b) {
    return (''+a).toUpperCase() < (''+b).toUpperCase() ? -1 : 1;
}

Le nullsera trié entre «nulk» et «nulm». Mais le undefinedsera toujours trié en dernier.

John Henckel
la source
(''+notdefined) === "undefined"il
triera donc
Je suppose que j'aurais dû chercher la définition de Array.prototype.sort: | parce que la partie sur (''+notdefined) === "undefined" est vraiment vraie ... ce qui signifie que si vous inversez les -1 et 1 dans la fonction de tri pour inverser l'ordre, les tris non définis continuent jusqu'à la fin. Il doit également être pris en compte lors de l'utilisation de la fonction de comparaison en dehors du contexte d'un tri de tableau (comme je l'étais lorsque je suis tombé sur cette question).
MattW
Et après avoir réfléchi à cette Array.prototype.sortdéfinition - quelques commentaires supplémentaires. Tout d'abord, il n'est pas nécessaire que (''+a)- ECMAScript nécessite toString()d'être appelé sur les éléments avant de les passer dans compareFn. Deuxièmement, le fait que ignoreCaserenvoie 1lors de la comparaison de chaînes égales (y compris les chaînes égales mais pour le cas) signifie que la spécification ne définit pas le résultat s'il y a des valeurs en double (ce sera probablement bien juste avec des échanges inutiles, je pense).
MattW
@MattW, Il me semble que undefinedc'est un cas particulier qui, pour tout x x <undefined et x> undefined, est à la fois faux . C'est undefinedtoujours le dernier, est un sous-produit de l'implémentation de tri de sorte. J'ai essayé de changer le ('' + a) en un simple, mais cela échoue. je reçois TypeError: a.toUpperCase is not a function. Apparemment, il toStringn'est pas appelé avant d'appeler compareFn.
John Henckel
1
Ah, ok, c'est parfaitement logique. Car undefinedle compareFn n'est jamais appelé
John Henckel
1

À l'appui de la réponse acceptée, je voudrais ajouter que la fonction ci-dessous semble changer les valeurs du tableau d'origine à trier de sorte que non seulement elle triera les minuscules, mais les valeurs en majuscules seront également modifiées en minuscules. C'est un problème pour moi car même si je souhaite voir Marie à côté de marie, je ne souhaite pas que le cas de la première valeur Mary soit changé en minuscule.

myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

Dans mes expériences, la fonction suivante de la réponse acceptée trie correctement mais ne change pas les valeurs.

["Foo", "bar"].sort(function (a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
});
John Shearing
la source
0

Cela peut être utile si vous avez du mal à comprendre:

var array = ["sort", "Me", "alphabetically", "But", "Ignore", "case"];
console.log('Unordered array ---', array, '------------');

array.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    console.log("Compare '" + a + "' and '" + b + "'");

    if( a == b) {
        console.log('Comparison result, 0 --- leave as is ');
        return 0;
    }
    if( a > b) {
        console.log('Comparison result, 1 --- move '+b+' to before '+a+' ');
        return 1;
    }
    console.log('Comparison result, -1 --- move '+a+' to before '+b+' ');
    return -1;


});

console.log('Ordered array ---', array, '------------');


// return logic

/***
If compareFunction(a, b) is less than 0, sort a to a lower index than b, i.e. a comes first.
If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
If compareFunction(a, b) is greater than 0, sort b to a lower index than a.
***/

http://jsfiddle.net/ianjamieson/wmxn2ram/1/

Ian Jamieson
la source
0
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if( a == b) return 0;
    if( a > b) return 1;
    return -1;
});

Dans la fonction ci-dessus, si nous comparons simplement lorsque les deux valeurs minuscules a et b, nous n'aurons pas le joli résultat.

Exemple, si le tableau est [A, a, B, b, c, C, D, d, e, E] et que nous utilisons la fonction ci-dessus, nous avons exactement ce tableau. Cela n'a rien changé.

Pour avoir le résultat est [A, a, B, b, C, c, D, d, E, e], nous devons comparer à nouveau lorsque deux valeurs minuscules sont égales:

function caseInsensitiveComparator(valueA, valueB) {
    var valueALowerCase = valueA.toLowerCase();
    var valueBLowerCase = valueB.toLowerCase();

    if (valueALowerCase < valueBLowerCase) {
        return -1;
    } else if (valueALowerCase > valueBLowerCase) {
        return 1;
    } else { //valueALowerCase === valueBLowerCase
        if (valueA < valueB) {
            return -1;
        } else if (valueA > valueB) {
            return 1;
        } else {
            return 0;
        }
    }
}
Envie
la source
-1

J'ai enveloppé la première réponse dans un polyfill afin que je puisse appeler .sortIgnoreCase () sur des tableaux de chaînes

// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
    Array.prototype.sortIgnoreCase = function () {
        return this.sort(function (a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
        });
    };
}
Jason
la source
Veuillez ne jamais faire ça. Modifiez uniquement le prototype des objets que vous possédez. Ce n'est pas non plus un polyfill, car cette méthode de tableau n'est nulle part dans les spécifications ECMAScript.
Joe Maffei
-2

Enveloppez vos cordes / /i. C'est un moyen facile d'utiliser l'expression régulière pour ignorer le boîtier

user3225968
la source
La question concerne le tri, pas la correspondance.
user4642212