Wie führe ich eine Sortierung ohne Berücksichtigung der Groß- und Kleinschreibung in JavaScript durch?

220

Ich habe eine Reihe von Zeichenfolgen, die ich in JavaScript sortieren muss, jedoch ohne Berücksichtigung der Groß- und Kleinschreibung. Wie führe ich das durch?

Jérôme Verstrynge
quelle

Antworten:

404

In (fast :) einem Einzeiler

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

Was in ... endet

[ 'bar', 'Foo' ]

Während

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

führt zu

[ 'Foo', 'bar' ]
Ivan Krechetov
quelle
9
Beachten Sie, dass die erweiterten Optionen von localeCompare noch nicht auf allen Plattformen / Browsern unterstützt werden. Ich weiß, dass sie in diesem Beispiel nicht verwendet werden, sondern nur der Klarheit halber hinzugefügt werden sollen. Siehe MDN für weitere Informationen
Ayame__
97
Wenn du gehst , localeCompare () einzubinden, können Sie nur verwenden , seine Fähigkeit , Groß- und Kleinschreibung zu sein, zum Beispiel:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Michael Dyck
2
+1 für nicht anrufen, toLowerCase()wenn localeComparedies in einigen Fällen bereits standardmäßig erfolgt. Weitere
Informationen zu
3
@Milimetrisch Entsprechend der Seite, auf die verwiesen wird, wird diese Funktion von einigen Browsern (z. B. IE <11 oder Safari) nicht unterstützt. Die hier erwähnte Lösung ist sehr gut, würde aber für einige Browser immer noch Backporting / Polyfill erfordern.
3k
2
Wenn Sie ein großes Array haben, ist es sinnvoll, es items.sort(new Intl.Collator('en').compare)für eine bessere Leistung zu verwenden. (Siehe 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: Bitte beachten Sie, dass ich dies ursprünglich geschrieben habe, um die Technik zu veranschaulichen, anstatt die Leistung im Auge zu behalten. Eine kompaktere Lösung finden Sie auch in der Antwort @Ivan Krechetov.

Ron Tornambe
quelle
3
Dies kann toLowerCasezweimal für jede Zeichenfolge aufgerufen werden. Es wäre effizienter, abgesenkte Versionen der Zeichenfolge in Variablen zu speichern.
Jacob
Richtig und danke. Ich habe dies mit Blick auf Klarheit geschrieben, nicht mit Leistung. Ich denke, das sollte ich beachten.
Ron Tornambe
1
@Jacob Um fair zu sein, hat die akzeptierte Antwort das gleiche Grundproblem: Sie kann möglicherweise .toLowerCase()mehrmals für jedes Element im Array aufrufen . Zum Beispiel rufen 45 die Vergleichsfunktion auf, wenn 10 Elemente in umgekehrter Reihenfolge sortiert werden. 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
nichts ist
47

Es ist Zeit, diese alte Frage erneut zu prüfen.

Sie sollten keine Lösungen verwenden, auf die Sie sich verlassen können toLowerCase. Sie sind ineffizient und funktionieren in einigen Sprachen (z. B. Türkisch) einfach nicht . Bevorzugen Sie dies:

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

Überprüfen Sie die Dokumentation auf Browserkompatibilität und alles, was Sie über die sensitivityOption wissen müssen .

ZunTzu
quelle
1
Seien Sie vorsichtig, dies wird nicht in allen Javascript-Engines unterstützt.
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 the Dark Absol
quelle
1
oderreturn a === b ? 0 : a > b ? 1 : -1;
Devin G Rhode
Dies funktioniert wahrscheinlich nicht wie vorgesehen für Zeichenfolgen, die Zahlen darstellen. Die arithmetischen Operatoren verwenden die Semantik von Zahlen anstelle von Zeichenfolgen. Wenn ja ["111", "33"], möchten wir möglicherweise, dass es zurückgegeben wird, ["111", "33"]da 1 in der Reihenfolge der Zeichencodes vor 3 steht. Die Funktion in dieser Antwort wird jedoch zurückgegeben, ["33", "111"]da die Nummer 33kleiner als die Nummer ist 111.
Austin Davis
@AustinDavis "33" > "111" === trueund 33 > 111 === false. Es funktioniert wie vorgesehen.
Niet the Dark Absol
12

Sie können auch das neue verwenden Intl.Collator().compare, das pro MDN beim Sortieren von Arrays effizienter ist . Der Nachteil ist, dass es von älteren Browsern nicht unterstützt wird. MDN gibt an, dass es in Safari überhaupt nicht unterstützt wird. Muss überprüft werden, da es besagt, dassIntl.Collator unterstützt wird.

Wenn Sie eine große Anzahl von Zeichenfolgen vergleichen, z. B. beim Sortieren großer Arrays, ist es besser, ein Intl.Collator-Objekt zu erstellen und die durch die compare-Eigenschaft bereitgestellte Funktion zu verwenden

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

Wenn Sie unabhängig von der Reihenfolge der Elemente im Eingabearray dieselbe Reihenfolge garantieren möchten, finden Sie hier eine stabile Sortierung:

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
quelle
5

Normalisieren Sie den Fall in der .sort()mit .toLowerCase().


quelle
4

Sie können auch den Elvis-Operator verwenden:

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

Gibt:

biscuit,Bob,charley,fudge,Fudge

Die localeCompare-Methode ist wahrscheinlich in Ordnung ...

Hinweis: Der Elvis-Operator ist eine Kurzform 'ternärer Operator', wenn sonst, normalerweise mit Zuweisung.
Wenn Sie das ?: Seitwärts betrachten, sieht es aus wie Elvis ...
dh anstelle von:

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

Sie können verwenden:

x = y?1:2;

dh wenn y wahr ist, geben Sie 1 zurück (für die Zuordnung zu x), andernfalls geben Sie 2 zurück (für die Zuordnung zu x).

AndyS
quelle
5
Um pedantisch zu sein, ist dies nicht der Elvis-Operator. Dies ist nur ein grundlegender ternärer Operator. Ein echter Elvis-Operator verschmilzt nicht, z. B. x = y ? y : zkönnen Sie dies tun x = y ?: z. Javascript hat keinen tatsächlichen Elvis-Operator, aber Sie können ihn x = y || zauf ähnliche Weise verwenden.
Charles Wood
3

Die anderen Antworten setzen voraus, dass das Array Zeichenfolgen enthält. Meine Methode ist besser, da sie auch dann funktioniert, wenn das Array null, undefinierte oder andere Nicht-Zeichenfolgen enthält.

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

Das nullwird zwischen 'nulk' und 'nulm' sortiert. Aber das undefinedwird immer zuletzt sortiert.

John Henckel
quelle
(''+notdefined) === "undefined"also würde es vor "z" sortieren
MattW
Ich denke, ich hätte die Definition von Array.prototype.sort: | nachschlagen sollen weil der Teil über (''+notdefined) === "undefined" wirklich wahr ist ... was bedeutet, wenn Sie -1 und 1 in der Sortierfunktion umdrehen, um die Reihenfolge umzukehren, wird undefined immer noch bis zum Ende sortiert. Dies muss auch berücksichtigt werden, wenn die Vergleichsfunktion außerhalb des Kontexts einer Array-Sortierung verwendet wird (wie ich es war, als ich auf diese Frage stieß).
MattW
Und nachdem ich jetzt über diese Array.prototype.sortDefinition nachgedacht habe - noch ein paar Kommentare. Erstens muss das (''+a)- ECMAScript toString()nicht für Elemente aufgerufen werden, bevor sie an compareFn übergeben werden. Zweitens ist die Tatsache , dass ignoreCaseErträge , 1wenn (einschließlich der Gleich-but-für-Fall) Strings gleich Vergleich bedeutet die Spezifikation das Ergebnis nicht definieren , wenn es doppelte Werte sind (wahrscheinlich in Ordnung sein nur mit einigen unnötigen Swaps auftritt, glaube ich).
MattW
@MattW, Es scheint mir, dass dies undefinedein Sonderfall ist, der für jedes x x <undefiniert und x> undefiniert beide falsch ist . Das undefinedist immer das Letzte, ist ein Nebenprodukt der Sortierimplementierung von sort. Ich habe versucht, das ('' + a) einfach in a zu ändern, aber es schlägt fehl. Ich verstehe TypeError: a.toUpperCase is not a function. Offenbar toStringwird nicht vor dem Aufruf von compareFn genannt.
John Henckel
1
Ah, ok, das macht vollkommen Sinn. Für undefinedden Vergleich wird Fn nie genannt
John Henckel
1

Zur Unterstützung der akzeptierten Antwort möchte ich hinzufügen, dass die folgende Funktion die Werte im zu sortierenden Original-Array so zu ändern scheint, dass nicht nur Kleinbuchstaben, sondern auch Großbuchstaben in Kleinbuchstaben sortiert werden. Dies ist ein Problem für mich, denn obwohl ich Mary neben Mary sehen möchte, möchte ich nicht, dass der Fall des ersten Wertes Mary in Kleinbuchstaben geändert wird.

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

In meinen Experimenten sortiert die folgende Funktion aus der akzeptierten Antwort korrekt, ändert jedoch die Werte nicht.

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

Dies kann hilfreich sein, wenn Sie Schwierigkeiten haben, Folgendes zu verstehen:

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
quelle
0
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if( a == b) return 0;
    if( a > b) return 1;
    return -1;
});

Wenn wir in der obigen Funktion nur vergleichen, wenn Kleinbuchstaben zwei Werte a und b sind, haben wir nicht das hübsche Ergebnis.

Beispiel: Wenn das Array [A, a, B, b, c, C, D, d, e, E] ist und wir die obige Funktion verwenden, haben wir genau dieses Array. Es hat nichts geändert.

Um das Ergebnis [A, a, B, b, C, c, D, d, E, e] zu erhalten, sollten wir erneut vergleichen, wenn zwei Kleinbuchstaben gleich sind:

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;
        }
    }
}
Neid
quelle
-1

Ich habe die oberste Antwort in eine Polyfüllung gefüllt, damit ich .sortIgnoreCase () für String-Arrays aufrufen kann

// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
    Array.prototype.sortIgnoreCase = function () {
        return this.sort(function (a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
        });
    };
}
Jason
quelle
Bitte mach das niemals. Ändern Sie nur den Prototyp von Dingen, die Sie besitzen. Dies ist auch keine Polyfüllung, da diese Array-Methode nirgends in den ECMAScript-Spezifikationen enthalten ist.
Joe Maffei
-2

Wickeln Sie Ihre Saiten ein / /i. Dies ist eine einfache Möglichkeit, Regex zu verwenden, um das Gehäuse zu ignorieren

user3225968
quelle
Die Frage ist über das Sortieren, nicht das Übereinstimmen.
user4642212