Wie man Strings alphabetisch nach Locale in JavaScript sortiert
Verwenden Sie Intl.Collator und localeCompare(), um Strings für jede Sprache korrekt zu sortieren
Einführung
Wenn Sie ein Array von Strings in JavaScript sortieren, vergleicht das Standardverhalten Strings anhand ihrer UTF-16-Codeeinheitswerte. Dies funktioniert für einfachen ASCII-Text, versagt jedoch bei der Sortierung von Namen, Produkttiteln oder beliebigem Text mit Akzentzeichen, nicht-lateinischen Schriften oder gemischten Groß- und Kleinbuchstaben.
Verschiedene Sprachen haben unterschiedliche Regeln für die alphabetische Reihenfolge. Schwedisch platziert å, ä und ö am Ende des Alphabets nach z. Deutsch behandelt ä in den meisten Kontexten als gleichwertig mit a. Französisch ignoriert in bestimmten Vergleichsmodi Akzente. Diese sprachlichen Regeln bestimmen, wie Menschen erwarten, sortierte Listen in ihrer Sprache zu sehen.
JavaScript bietet zwei APIs für sprachbewusste String-Sortierung. Die Methode String.prototype.localeCompare() behandelt einfache Vergleiche. Die Intl.Collator-API bietet bessere Leistung beim Sortieren großer Arrays. Diese Lektion erklärt, wie beide funktionieren, wann jede verwendet werden sollte und wie das Sortierverhalten für verschiedene Sprachen konfiguriert wird.
Warum die Standardsortierung bei internationalen Texten versagt
Die Standardmethode Array.sort() vergleicht Strings anhand ihrer UTF-16-Codeeinheitswerte. Das bedeutet, dass Großbuchstaben immer vor Kleinbuchstaben kommen und Zeichen mit Akzenten nach z sortiert werden.
const names = ['Åsa', 'Anna', 'Örjan', 'Bengt', 'Ärla'];
const sorted = names.sort();
console.log(sorted);
// Ausgabe: ['Anna', 'Bengt', 'Åsa', 'Ärla', 'Örjan']
Diese Ausgabe ist für Schwedisch falsch. Im Schwedischen sind å, ä und ö separate Buchstaben, die am Ende des Alphabets stehen. Die korrekte Reihenfolge sollte Anna zuerst platzieren, dann Bengt, dann Åsa, Ärla und Örjan.
Das Problem tritt auf, weil die Standardsortierung Codepunktwerte vergleicht, nicht die sprachliche Bedeutung. Der Buchstabe Å hat den Codepunkt U+00C5, der größer ist als der Codepunkt für z (U+007A). JavaScript hat keine Möglichkeit zu wissen, dass schwedische Sprecher Å als separaten Buchstaben mit einer bestimmten Position im Alphabet betrachten.
Gemischte Groß- und Kleinschreibung erzeugt ein weiteres Problem.
const words = ['zebra', 'Apple', 'banana', 'Zoo'];
const sorted = words.sort();
console.log(sorted);
// Ausgabe: ['Apple', 'Zoo', 'banana', 'zebra']
Alle Großbuchstaben haben niedrigere Codepunktwerte als Kleinbuchstaben. Dies führt dazu, dass Apple und Zoo vor banana erscheinen, was in keiner Sprache der alphabetischen Reihenfolge entspricht.
Wie localeCompare Zeichenketten nach linguistischen Regeln sortiert
Die Methode localeCompare() vergleicht zwei Zeichenketten gemäß den Sortierregeln einer bestimmten Locale. Sie gibt eine negative Zahl zurück, wenn die erste Zeichenkette vor der zweiten kommt, null, wenn sie gleichwertig sind, und eine positive Zahl, wenn die erste Zeichenkette nach der zweiten kommt.
const result = 'a'.localeCompare('b', 'en-US');
console.log(result);
// Ausgabe: -1 (negativ bedeutet 'a' kommt vor 'b')
Sie können localeCompare() direkt mit Array.sort() verwenden, indem Sie es als Vergleichsfunktion übergeben.
const names = ['Åsa', 'Anna', 'Örjan', 'Bengt', 'Ärla'];
const sorted = names.sort((a, b) => a.localeCompare(b, 'sv-SE'));
console.log(sorted);
// Ausgabe: ['Anna', 'Bengt', 'Åsa', 'Ärla', 'Örjan']
Die schwedische Locale platziert Anna und Bengt zuerst, da sie standardmäßige lateinische Buchstaben verwenden. Dann folgen Åsa, Ärla und Örjan mit ihren speziellen schwedischen Buchstaben am Ende.
Dieselbe Liste, sortiert mit einer deutschen Locale, erzeugt unterschiedliche Ergebnisse.
const names = ['Åsa', 'Anna', 'Örjan', 'Bengt', 'Ärla'];
const sorted = names.sort((a, b) => a.localeCompare(b, 'de-DE'));
console.log(sorted);
// Ausgabe: ['Anna', 'Ärla', 'Åsa', 'Bengt', 'Örjan']
Deutsch behandelt ä für Sortierzwecke als gleichwertig mit a. Dies platziert Ärla direkt nach Anna, anstatt am Ende wie im Schwedischen.
Wann localeCompare verwendet werden sollte
Verwenden Sie localeCompare(), wenn Sie ein kleines Array sortieren oder zwei Zeichenketten vergleichen müssen. Es bietet eine einfache API, ohne dass Sie ein Collator-Objekt erstellen und verwalten müssen.
const items = ['Banana', 'apple', 'Cherry'];
const sorted = items.sort((a, b) => a.localeCompare(b, 'en-US'));
console.log(sorted);
// Ausgabe: ['apple', 'Banana', 'Cherry']
Dieser Ansatz funktioniert gut für Arrays mit einigen Dutzend Elementen. Die Auswirkungen auf die Leistung sind für kleine Datensätze vernachlässigbar.
Sie können localeCompare() auch verwenden, um zu prüfen, ob eine Zeichenkette vor einer anderen kommt, ohne ein ganzes Array zu sortieren.
const firstName = 'Åsa';
const secondName = 'Anna';
if (firstName.localeCompare(secondName, 'sv-SE') < 0) {
console.log(`${firstName} kommt vor ${secondName}`);
} else {
console.log(`${secondName} kommt vor ${firstName}`);
}
// Ausgabe: "Anna kommt vor Åsa"
Dieser Vergleich respektiert die schwedische alphabetische Reihenfolge, ohne dass ein vollständiges Array sortiert werden muss.
Wie Intl.Collator die Leistung verbessert
Die Intl.Collator-API erstellt eine wiederverwendbare Vergleichsfunktion, die für wiederholte Verwendung optimiert ist. Beim Sortieren großer Arrays oder bei vielen Vergleichen ist ein Collator deutlich schneller als der Aufruf von localeCompare() für jeden Vergleich.
const collator = new Intl.Collator('sv-SE');
const names = ['Åsa', 'Anna', 'Örjan', 'Bengt', 'Ärla'];
const sorted = names.sort(collator.compare);
console.log(sorted);
// Output: ['Anna', 'Bengt', 'Åsa', 'Ärla', 'Örjan']
Die Eigenschaft collator.compare gibt eine Vergleichsfunktion zurück, die direkt mit Array.sort() funktioniert. Sie müssen sie nicht in eine Pfeilfunktion einpacken.
Durch die einmalige Erstellung eines Collators und dessen Wiederverwendung für mehrere Operationen wird der Overhead vermieden, der bei jedem Vergleich durch das Nachschlagen von Locale-Daten entsteht.
const collator = new Intl.Collator('de-DE');
const germanCities = ['München', 'Berlin', 'Köln', 'Hamburg'];
const sortedCities = germanCities.sort(collator.compare);
const germanNames = ['Müller', 'Schmidt', 'Schröder', 'Fischer'];
const sortedNames = germanNames.sort(collator.compare);
console.log(sortedCities);
// Output: ['Berlin', 'Hamburg', 'Köln', 'München']
console.log(sortedNames);
// Output: ['Fischer', 'Müller', 'Schmidt', 'Schröder']
Derselbe Collator verarbeitet beide Arrays, ohne dass eine neue Instanz erstellt werden muss.
Wann Intl.Collator verwendet werden sollte
Verwenden Sie Intl.Collator beim Sortieren von Arrays mit Hunderten oder Tausenden von Elementen. Der Leistungsvorteil steigt mit der Array-Größe, da die Vergleichsfunktion während des Sortierens viele Male aufgerufen wird.
const collator = new Intl.Collator('en-US');
const products = [/* Array mit 10.000 Produktnamen */];
const sorted = products.sort(collator.compare);
Für Arrays mit mehr als einigen hundert Elementen kann der Collator mehrmals schneller sein als localeCompare().
Verwenden Sie Intl.Collator auch, wenn Sie mehrere Arrays mit demselben Gebietsschema und denselben Optionen sortieren müssen. Die einmalige Erstellung des Collators und seine Wiederverwendung eliminiert wiederholte Nachschlagungen von Locale-Daten.
const collator = new Intl.Collator('fr-FR');
const firstNames = ['Amélie', 'Bernard', 'Émilie', 'François'];
const lastNames = ['Dubois', 'Martin', 'Lefèvre', 'Bernard'];
const sortedFirstNames = firstNames.sort(collator.compare);
const sortedLastNames = lastNames.sort(collator.compare);
Dieses Muster eignet sich gut für die Erstellung von Tabellenansichten oder anderen Benutzeroberflächen, die mehrere sortierte Listen anzeigen.
Wie man die Locale spezifiziert
Sowohl localeCompare() als auch Intl.Collator akzeptieren einen Locale-Identifier als erstes Argument. Dieser Identifier verwendet das BCP 47-Format, typischerweise eine Kombination aus Sprachcode und optionalem Regionscode.
const names = ['Åsa', 'Anna', 'Ärla'];
// Schwedische Locale
const swedishSorted = names.sort((a, b) => a.localeCompare(b, 'sv-SE'));
console.log(swedishSorted);
// Ausgabe: ['Anna', 'Åsa', 'Ärla']
// Deutsche Locale
const germanSorted = names.sort((a, b) => a.localeCompare(b, 'de-DE'));
console.log(germanSorted);
// Ausgabe: ['Anna', 'Ärla', 'Åsa']
Die Locale bestimmt, welche Kollationsregeln angewendet werden. Schwedisch und Deutsch haben unterschiedliche Regeln für å und ä, was zu unterschiedlichen Sortierreihenfolgen führt.
Sie können die Locale weglassen, um die Standardlocale des Benutzers aus dem Browser zu verwenden.
const collator = new Intl.Collator();
const names = ['Åsa', 'Anna', 'Ärla'];
const sorted = names.sort(collator.compare);
Dieser Ansatz respektiert die Sprachpräferenzen des Benutzers, ohne eine spezifische Locale fest zu codieren. Die sortierte Reihenfolge entspricht den Erwartungen des Benutzers basierend auf seinen Browsereinstellungen.
Sie können auch ein Array von Locales übergeben, um Fallback-Optionen bereitzustellen.
const collator = new Intl.Collator(['sv-SE', 'sv', 'en-US']);
Die API verwendet die erste unterstützte Locale aus dem Array. Wenn Schwedisch aus Schweden nicht verfügbar ist, versucht sie generisches Schwedisch und fällt dann auf US-Englisch zurück.
Wie man die Groß-/Kleinschreibung steuert
Die Option sensitivity bestimmt, wie der Vergleich Unterschiede in Groß-/Kleinschreibung und Akzenten behandelt. Sie akzeptiert vier Werte: base, accent, case und variant.
Die base-Sensitivität ignoriert sowohl Groß-/Kleinschreibung als auch Akzente und vergleicht nur die Grundzeichen.
const collator = new Intl.Collator('en-US', { sensitivity: 'base' });
console.log(collator.compare('a', 'A'));
// Ausgabe: 0 (gleich)
console.log(collator.compare('a', 'á'));
// Ausgabe: 0 (gleich)
console.log(collator.compare('a', 'b'));
// Ausgabe: -1 (unterschiedliche Grundzeichen)
Dieser Modus behandelt a, A und á als identisch, da sie dasselbe Grundzeichen teilen.
Die accent-Sensitivität berücksichtigt Akzente, ignoriert aber die Groß-/Kleinschreibung.
const collator = new Intl.Collator('en-US', { sensitivity: 'accent' });
console.log(collator.compare('a', 'A'));
// Ausgabe: 0 (gleich, Groß-/Kleinschreibung ignoriert)
console.log(collator.compare('a', 'á'));
// Ausgabe: -1 (unterschiedlich, Akzent ist relevant)
Die case-Sensitivität berücksichtigt die Groß-/Kleinschreibung, ignoriert aber Akzente.
const collator = new Intl.Collator('en-US', { sensitivity: 'case' });
console.log(collator.compare('a', 'A'));
// Ausgabe: -1 (unterschiedlich, Groß-/Kleinschreibung ist relevant)
console.log(collator.compare('a', 'á'));
// Ausgabe: 0 (gleich, Akzent ignoriert)
Die variant-Sensitivität (Standard) berücksichtigt alle Unterschiede.
const collator = new Intl.Collator('en-US', { sensitivity: 'variant' });
console.log(collator.compare('a', 'A'));
// Ausgabe: -1 (unterschiedlich)
console.log(collator.compare('a', 'á'));
// Ausgabe: -1 (unterschiedlich)
Dieser Modus bietet den strengsten Vergleich und behandelt jeden Unterschied als signifikant.
Wie man Zeichenketten mit eingebetteten Zahlen sortiert
Die Option numeric ermöglicht die numerische Sortierung für Zeichenketten, die Zahlen enthalten. Wenn aktiviert, behandelt der Vergleich Ziffernfolgen als numerische Werte, anstatt sie Zeichen für Zeichen zu vergleichen.
const files = ['file1.txt', 'file10.txt', 'file2.txt', 'file20.txt'];
// Standardsortierung (falsche Reihenfolge)
const defaultSorted = [...files].sort();
console.log(defaultSorted);
// Ausgabe: ['file1.txt', 'file10.txt', 'file2.txt', 'file20.txt']
// Numerische Sortierung (korrekte Reihenfolge)
const collator = new Intl.Collator('en-US', { numeric: true });
const numericSorted = files.sort(collator.compare);
console.log(numericSorted);
// Ausgabe: ['file1.txt', 'file2.txt', 'file10.txt', 'file20.txt']
Ohne numerische Sortierung werden die Zeichenketten Zeichen für Zeichen sortiert. Die Zeichenkette 10 kommt vor 2, weil das erste Zeichen 1 einen niedrigeren Codepoint als 2 hat.
Mit aktivierter numerischer Sortierung erkennt der Collator 10 als die Zahl zehn und 2 als die Zahl zwei. Dies erzeugt die erwartete Sortierreihenfolge, bei der 2 vor 10 kommt.
Diese Option ist nützlich für die Sortierung von Dateinamen, Versionsnummern oder beliebigen Zeichenketten, die Text und Zahlen kombinieren.
const versions = ['v1.10', 'v1.2', 'v1.20', 'v1.3'];
const collator = new Intl.Collator('en-US', { numeric: true });
const sorted = versions.sort(collator.compare);
console.log(sorted);
// Ausgabe: ['v1.2', 'v1.3', 'v1.10', 'v1.20']
Wie man steuert, welcher Schreibfall zuerst kommt
Die Option caseFirst bestimmt, ob Groß- oder Kleinbuchstaben zuerst sortiert werden, wenn Zeichenketten verglichen werden, die sich nur im Schreibfall unterscheiden. Sie akzeptiert drei Werte: upper, lower oder false.
const words = ['apple', 'Apple', 'APPLE'];
// Großbuchstaben zuerst
const upperFirst = new Intl.Collator('en-US', { caseFirst: 'upper' });
const upperSorted = [...words].sort(upperFirst.compare);
console.log(upperSorted);
// Ausgabe: ['APPLE', 'Apple', 'apple']
// Kleinbuchstaben zuerst
const lowerFirst = new Intl.Collator('en-US', { caseFirst: 'lower' });
const lowerSorted = [...words].sort(lowerFirst.compare);
console.log(lowerSorted);
// Ausgabe: ['apple', 'Apple', 'APPLE']
// Standard (lokaleabhängig)
const defaultCase = new Intl.Collator('en-US', { caseFirst: 'false' });
const defaultSorted = [...words].sort(defaultCase.compare);
console.log(defaultSorted);
// Ausgabe hängt von der Locale ab
Der Wert false verwendet die Standardreihenfolge der Schreibfälle der Locale. Die meisten Locales behandeln Zeichenketten, die sich nur im Schreibfall unterscheiden, als gleich, wenn Standardsensitivitätseinstellungen verwendet werden.
Diese Option hat nur dann eine Auswirkung, wenn die Option sensitivity es erlaubt, dass Unterschiede im Schreibfall relevant sind.
Wie man Interpunktion beim Sortieren ignoriert
Die Option ignorePunctuation weist den Collator an, Interpunktionszeichen beim Vergleich von Zeichenketten zu überspringen. Dies kann nützlich sein, wenn Titel oder Phrasen sortiert werden, die Interpunktion enthalten können oder auch nicht.
const titles = [
'The Old Man',
'The Old-Man',
'The Oldman',
];
// Standard (Interpunktion ist relevant)
const defaultCollator = new Intl.Collator('en-US');
const defaultSorted = [...titles].sort(defaultCollator.compare);
console.log(defaultSorted);
// Ausgabe: ['The Old Man', 'The Old-Man', 'The Oldman']
// Interpunktion ignorieren
const noPunctCollator = new Intl.Collator('en-US', { ignorePunctuation: true });
const noPunctSorted = [...titles].sort(noPunctCollator.compare);
console.log(noPunctSorted);
// Ausgabe: ['The Old Man', 'The Old-Man', 'The Oldman']
Wenn die Interpunktion ignoriert wird, behandelt der Vergleich den Bindestrich in "Old-Man" so, als ob er nicht existieren würde, wodurch die Zeichenketten verglichen werden, als wären sie alle "TheOldMan".
Sortieren von Benutzernamen aus verschiedenen Ländern
Beim Sortieren von Namen von Benutzern aus der ganzen Welt sollte das bevorzugte Gebietsschema des Benutzers verwendet werden, um dessen sprachliche Erwartungen zu respektieren.
const userLocale = navigator.language;
const collator = new Intl.Collator(userLocale);
const users = [
{ name: 'Müller', country: 'Germany' },
{ name: 'Martin', country: 'France' },
{ name: 'Andersson', country: 'Sweden' },
{ name: 'García', country: 'Spain' },
];
const sorted = users.sort((a, b) => collator.compare(a.name, b.name));
sorted.forEach(user => {
console.log(`${user.name} (${user.country})`);
});
Dieser Code erkennt das Gebietsschema des Benutzers aus dem Browser und sortiert die Namen entsprechend. Ein deutscher Benutzer sieht die Liste nach deutschen Regeln sortiert, während ein schwedischer Benutzer sie nach schwedischen Regeln sortiert sieht.
Sortieren mit Gebietsschema-Wechsel
Wenn Ihre Anwendung es Benutzern ermöglicht, die Sprache zu wechseln, aktualisieren Sie den Collator, wenn sich das Gebietsschema ändert.
let currentLocale = 'en-US';
let collator = new Intl.Collator(currentLocale);
function setLocale(newLocale) {
currentLocale = newLocale;
collator = new Intl.Collator(currentLocale);
}
function sortItems(items) {
return items.sort(collator.compare);
}
// Benutzer wechselt zu Schwedisch
setLocale('sv-SE');
const names = ['Åsa', 'Anna', 'Örjan'];
console.log(sortItems(names));
// Ausgabe: ['Anna', 'Åsa', 'Örjan']
// Benutzer wechselt zu Deutsch
setLocale('de-DE');
const germanNames = ['Über', 'Uhr', 'Udo'];
console.log(sortItems(germanNames));
// Ausgabe: ['Udo', 'Uhr', 'Über']
Dieses Muster stellt sicher, dass sortierte Listen aktualisiert werden, um der vom Benutzer ausgewählten Sprache zu entsprechen.
Wahl zwischen localeCompare und Intl.Collator
Verwenden Sie localeCompare(), wenn Sie einen schnellen einmaligen Vergleich benötigen oder ein kleines Array mit weniger als 100 Elementen sortieren. Die einfachere Syntax ist leichter zu lesen und der Leistungsunterschied ist bei kleinen Datensätzen vernachlässigbar.
const items = ['banana', 'Apple', 'cherry'];
const sorted = items.sort((a, b) => a.localeCompare(b, 'en-US'));
Verwenden Sie Intl.Collator, wenn Sie große Arrays sortieren, viele Vergleiche durchführen oder mehrere Arrays mit denselben Locale- und Optionseinstellungen sortieren. Das einmalige Erstellen des Collators und seine Wiederverwendung bietet bessere Leistung.
const collator = new Intl.Collator('en-US', { sensitivity: 'base', numeric: true });
const products = [/* großes Array */];
const sorted = products.sort(collator.compare);
Beide Ansätze liefern die gleichen Ergebnisse. Die Wahl hängt von Ihren Leistungsanforderungen und Präferenzen bei der Codeorganisation ab.