Come eseguire l'ordinamento senza distinzione tra maiuscole e minuscole in JavaScript?


Risposte:


404

In (quasi :) un one-liner

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

Che si traduce in

[ 'bar', 'Foo' ]

Mentre

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

risultati in

[ 'Foo', 'bar' ]

9
Ricorda che le opzioni avanzate di localeCompare non sono ancora supportate su tutte le piattaforme / browser. So che non sono usati in questo esempio, ma volevo solo aggiungere per chiarezza. Vedi MDN per maggiori informazioni
Ayame__

97
Se hai intenzione di coinvolgere localeCompare (), potresti semplicemente usare la sua capacità di non distinguere tra maiuscole e minuscole, ad esempio:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Michael Dyck,

2
+1 per non chiamare toLowerCase()quando lo localeComparefa già di default in alcuni casi. Puoi leggere di più sui parametri per passarci qui: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Milimetric

3
@ Accordo milimetrico con la pagina di riferimento, questa funzione non è supportata da alcuni browser (ad es. IE <11 o Safari). la soluzione menzionata qui è molto buona, ma richiederebbe comunque il backporting / polyfill per alcuni browser.
3k-

2
Se si dispone di un array di grandi dimensioni, ha senso utilizzare items.sort(new Intl.Collator('en').compare)per prestazioni migliori. (Vedi 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: Nota che inizialmente ho scritto questo per illustrare la tecnica piuttosto che avere in mente le prestazioni. Per una soluzione più compatta, consultare anche la risposta @Ivan Krechetov.


3
Questo può chiamare toLowerCasedue volte su ogni stringa; sarebbe più efficiente archiviare le versioni ridotte della stringa in variabili.
Jacob,

Vero e grazie. Ho scritto questo con chiarezza in mente, non prestazioni. Immagino che dovrei notare questo.
Ron Tornambe,

1
@Jacob Per essere onesti la risposta accettata ha lo stesso problema di base: può eventualmente chiamare .toLowerCase()più volte per ogni elemento dell'array. Ad esempio, 45 chiamate alla funzione di confronto durante l'ordinamento di 10 articoli in ordine inverso. 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
Nulla è necessario il

47

È tempo di rivisitare questa vecchia domanda.

Non utilizzare soluzioni basate su toLowerCase. Sono inefficienti e semplicemente non funzionano in alcune lingue (ad esempio il turco). Preferisco questo:

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

Controlla la documentazione per la compatibilità del browser e tutto ciò che c'è da sapere sensitivitysull'opzione.


1
Fai attenzione, questo non è supportato in tutti i motori 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;
});

1
oppurereturn a === b ? 0 : a > b ? 1 : -1;
Devin G Rhode,

Questo probabilmente non funzionerà come previsto per le stringhe che rappresentano numeri. Gli operatori aritmetici useranno la semantica dei numeri anziché le stringhe. Ad esempio, se lo abbiamo ["111", "33"], potremmo desiderare che ritorni ["111", "33"]perché 1 precede 3 nell'ordinamento del codice carattere. Tuttavia, la funzione in questa risposta tornerà ["33", "111"]perché il numero 33è inferiore al numero 111.
Austin Davis,

@AustinDavis "33" > "111" === truee 33 > 111 === false. Funziona come previsto.
Niet the Dark Absol,

12

È inoltre possibile utilizzare il nuovo Intl.Collator().compare, per MDN è più efficiente quando si ordinano gli array. Il rovescio della medaglia è che non è supportato dai browser più vecchi. MDN afferma che non è affatto supportato in Safari. È necessario verificarlo, poiché afferma che Intl.Collatorè supportato.

Quando si confrontano un numero elevato di stringhe, ad esempio nell'ordinamento di matrici di grandi dimensioni, è meglio creare un oggetto Intl.Collator e utilizzare la funzione fornita dalla sua proprietà compare

["Foo", "bar"].sort(Intl.Collator().compare); //["bar", "Foo"]

11

Se si desidera garantire lo stesso ordine indipendentemente dall'ordine degli elementi nell'array di input, ecco un ordinamento stabile :

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;
});

5

Normalizza il caso .sort()con .toLowerCase().


4

Puoi anche usare l'operatore 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);

dà:

biscuit,Bob,charley,fudge,Fudge

Il metodo localeCompare probabilmente va bene anche se ...

Nota: l'operatore Elvis è un 'operatore ternario' in forma abbreviata per se poi, di solito con incarico.
Se guardi il?: Lateralmente, sembra Elvis ...
cioè invece di:

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

Puoi usare:

x = y?1:2;

cioè quando y è vero, quindi restituisce 1 (per l'assegnazione a x), altrimenti restituisce 2 (per l'assegnazione a x).


5
Per essere pedanti, questo non è l'operatore Elvis. Questo è solo un operatore ternario di base. Un vero operatore Elvis è a coalescenza nulla, ad esempio, invece di x = y ? y : z, puoi farlo x = y ?: z. Javascript non ha un vero operatore Elvis, ma puoi usarlo x = y || zin modo simile.
Charles Wood,

3

Le altre risposte presuppongono che l'array contenga stringhe. Il mio metodo è migliore, perché funzionerà anche se l'array contiene null, non definito o altre non stringhe.

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;
}

Il nullverranno ordinati tra 'nulk' e 'nulm'. Ma undefinedsarà sempre ordinato per ultimo.


(''+notdefined) === "undefined"quindi sarebbe
ordinato

Immagino che avrei dovuto cercare la definizione di Array.prototype.sort: | perché la parte su (''+notdefined) === "undefined" è davvero vera ... il che significa che se capovolgi -1 e 1 nella funzione di ordinamento per invertire l'ordine, undefined ordina comunque fino alla fine. Deve anche essere preso in considerazione quando si utilizza la funzione di confronto al di fuori del contesto di un ordinamento di array (come lo ero quando sono arrivato a questa domanda).
Matt,

E dopo aver ponderato questa Array.prototype.sortdefinizione, abbina più commenti. Innanzitutto, non è necessario che (''+a)- ECMAScript richieda toString()di essere richiamato su elementi prima di passarli in compareFn. In secondo luogo, il fatto che ignoreCaseritorni 1quando si confrontano stringhe uguali (incluso uguale per maiuscole e minuscole) significa che la specifica non definisce il risultato se ci sono valori duplicati (probabilmente andrà bene solo con alcuni swap non necessari, penso).
Matt,

@MattW, mi sembra che undefinedsia un caso speciale, che per ogni x x <indefinito e x> indefinito sono entrambi falsi . Questo undefinedè sempre l'ultimo, è un sottoprodotto dell'implementazione dell'ordinamento dell'ordinamento. Ho provato a cambiare ('' + a) in semplicemente a, ma non ci riesce. ho capito TypeError: a.toUpperCase is not a function. Apparentemente nontoString viene chiamato prima di chiamare compareFn.
John Henckel,

1
Ah, ok, ha perfettamente senso. Per undefinedil compareFn non
John Henckel,


1

A sostegno della risposta accettata, vorrei aggiungere che la seguente funzione sembra modificare i valori nell'array originale da ordinare in modo che non solo ordinerà le lettere minuscole, ma anche i valori maiuscoli verranno modificati in lettere minuscole. Questo è un problema per me perché anche se desidero vedere Mary accanto a Mary, non desidero che il caso del primo valore Mary sia cambiato in minuscolo.

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

Nei miei esperimenti, la seguente funzione della risposta accettata ordina correttamente ma non cambia i valori.

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

0

Questo può aiutare se hai difficoltà a capire:

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/


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

Nella funzione sopra, se confrontiamo solo quando due lettere minuscole valgono aeb, non avremo il risultato grazioso.

Esempio, se array è [A, a, B, b, c, C, D, d, e, E] e utilizziamo la funzione sopra, abbiamo esattamente quell'array. Non è cambiato nulla.

Per avere il risultato è [A, a, B, b, C, c, D, d, E, e], dovremmo confrontare nuovamente quando due valori minuscoli sono uguali:

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;
        }
    }
}

-1

Ho racchiuso la risposta superiore in un polyfill in modo da poter chiamare .sortIgnoreCase () su array di stringhe

// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
    Array.prototype.sortIgnoreCase = function () {
        return this.sort(function (a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
        });
    };
}

Per favore, non farlo mai. Modifica solo il prototipo delle cose che possiedi. Anche questo non è un polyfill, poiché questo metodo Array non è presente nelle specifiche ECMAScript.
Joe Maffei,

-2

Avvolgi le corde / /i. Questo è un modo semplice per usare regex per ignorare l'involucro


La domanda riguarda l'ordinamento, non la corrispondenza.
user4642212
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.