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-Code-Unit-Werte. Dies funktioniert für einfachen ASCII-Text, versagt jedoch beim Sortieren von Namen, Produkttiteln oder beliebigem Text, der Zeichen mit Akzenten, nicht-lateinische Schriften oder gemischte Groß- und Kleinbuchstaben enthält.
Verschiedene Sprachen haben unterschiedliche Regeln für die alphabetische Reihenfolge. Im Schwedischen werden å, ä und ö am Ende des Alphabets nach z platziert. Im Deutschen wird ä in den meisten Kontexten als äquivalent zu a behandelt. Im Französischen werden Akzente in bestimmten Vergleichsmodi ignoriert. Diese linguistischen Regeln bestimmen, wie Menschen erwarten, sortierte Listen in ihrer Sprache zu sehen.
JavaScript bietet zwei APIs für locale-bewusstes String-Sortieren. Die Methode String.prototype.localeCompare() behandelt einfache Vergleiche. Die API Intl.Collator bietet bessere Performance beim Sortieren großer Arrays. Diese Lektion erklärt, wie beide funktionieren, wann welche verwendet werden sollte und wie das Sortierverhalten für verschiedene Sprachen konfiguriert wird.
Warum die Standardsortierung für internationalen Text versagt
Die Standardmethode Array.sort() vergleicht Strings anhand ihrer UTF-16-Code-Unit-Werte. 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);
// Output: ['Anna', 'Bengt', 'Åsa', 'Ärla', 'Örjan']
Diese Ausgabe ist für Schwedisch falsch. Im Schwedischen sind å, ä und ö separate Buchstaben, die ans Ende des Alphabets gehören. Die korrekte Reihenfolge sollte Anna zuerst platzieren, dann Bengt, dann Åsa, Ärla und Örjan.
Das Problem tritt auf, weil die Standardsortierung Code-Point-Werte vergleicht, nicht die linguistische Bedeutung. Der Buchstabe Å hat den Code-Point U+00C5, der größer ist als der Code-Point für z (U+007A). JavaScript hat keine Möglichkeit zu wissen, dass schwedischsprachige Personen Å als separaten Buchstaben mit einer spezifischen 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);
// Output: ['Apple', 'Zoo', 'banana', 'zebra']
Alle Großbuchstaben haben niedrigere Code-Point-Werte als Kleinbuchstaben. Dies führt dazu, dass Apple und Zoo vor banana erscheinen, was in keiner Sprache der alphabetischen Reihenfolge entspricht.
Wie localeCompare Strings nach linguistischen Regeln sortiert
Die localeCompare()-Methode vergleicht zwei Strings gemäß den Sortierregeln einer bestimmten Locale. Sie gibt eine negative Zahl zurück, wenn der erste String vor dem zweiten kommt, null, wenn sie äquivalent sind, und eine positive Zahl, wenn der erste String nach dem zweiten kommt.
const result = 'a'.localeCompare('b', 'en-US');
console.log(result);
// Output: -1 (negative means 'a' comes before '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);
// Output: ['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, liefert unterschiedliche Ergebnisse.
const names = ['Åsa', 'Anna', 'Örjan', 'Bengt', 'Ärla'];
const sorted = names.sort((a, b) => a.localeCompare(b, 'de-DE'));
console.log(sorted);
// Output: ['Anna', 'Ärla', 'Åsa', 'Bengt', 'Örjan']
Deutsch behandelt ä für Sortierzwecke als äquivalent zu 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 Strings 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);
// Output: ['apple', 'Banana', 'Cherry']
Dieser Ansatz funktioniert gut für Arrays mit einigen Dutzend Elementen. Die Auswirkung auf die Performance ist bei kleinen Datensätzen vernachlässigbar.
Sie können localeCompare() auch verwenden, um zu prüfen, ob ein String vor einem anderen kommt, ohne ein gesamtes Array zu sortieren.
const firstName = 'Åsa';
const secondName = 'Anna';
if (firstName.localeCompare(secondName, 'sv-SE') < 0) {
console.log(`${firstName} comes before ${secondName}`);
} else {
console.log(`${secondName} comes before ${firstName}`);
}
// Output: "Anna comes before Åsa"
Dieser Vergleich respektiert die schwedische alphabetische Reihenfolge, ohne dass ein vollständiges Array sortiert werden muss.
Wie Intl.Collator die Performance verbessert
Die Intl.Collator-API erstellt eine wiederverwendbare Vergleichsfunktion, die für wiederholte Verwendung optimiert ist. Wenn Sie große Arrays sortieren oder viele Vergleiche durchführen, ist ein Collator deutlich schneller als das Aufrufen 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 collator.compare-Property gibt eine Vergleichsfunktion zurück, die direkt mit Array.sort() funktioniert. Sie müssen sie nicht in eine Arrow-Funktion einbetten.
Das einmalige Erstellen eines Collators und dessen Wiederverwendung für mehrere Operationen vermeidet den Overhead des Nachschlagens von Locale-Daten bei jedem Vergleich.
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, wenn Sie Arrays mit Hunderten oder Tausenden von Elementen sortieren. Der Performance-Vorteil 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 with 10,000 product names */];
const sorted = products.sort(collator.compare);
Bei Arrays mit mehr als einigen hundert Elementen kann der Collator mehrfach schneller sein als localeCompare().
Verwenden Sie Intl.Collator auch, wenn Sie mehrere Arrays mit derselben Locale und denselben Optionen sortieren müssen. Das einmalige Erstellen des Collators und dessen Wiederverwendung eliminiert wiederholte Locale-Daten-Lookups.
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 Pattern funktioniert gut beim Erstellen von Tabellenansichten oder anderen Interfaces, die mehrere sortierte Listen anzeigen.
Wie die Locale angegeben wird
Sowohl localeCompare() als auch Intl.Collator akzeptieren einen Locale-Identifier als erstes Argument. Dieser Identifier verwendet das BCP-47-Format und kombiniert typischerweise einen Sprachcode mit einem optionalen Regionscode.
const names = ['Åsa', 'Anna', 'Ärla'];
// Swedish locale
const swedishSorted = names.sort((a, b) => a.localeCompare(b, 'sv-SE'));
console.log(swedishSorted);
// Output: ['Anna', 'Åsa', 'Ärla']
// German locale
const germanSorted = names.sort((a, b) => a.localeCompare(b, 'de-DE'));
console.log(germanSorted);
// Output: ['Anna', 'Ärla', 'Åsa']
Das Gebietsschema bestimmt, welche Sortierregeln gelten. Schwedisch und Deutsch haben unterschiedliche Regeln für å und ä, was zu unterschiedlichen Sortierreihenfolgen führt.
Sie können das Gebietsschema weglassen, um das Standard-Gebietsschema 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 Spracheinstellungen des Benutzers, ohne ein bestimmtes Gebietsschema fest zu codieren. Die Sortierreihenfolge entspricht dem, was der Benutzer basierend auf seinen Browsereinstellungen erwartet.
Sie können auch ein Array von Gebietsschemata übergeben, um Fallback-Optionen bereitzustellen.
const collator = new Intl.Collator(['sv-SE', 'sv', 'en-US']);
Die API verwendet das erste unterstützte Gebietsschema 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 Sensitivität base ignoriert sowohl Groß-/Kleinschreibung als auch Akzente und vergleicht nur die Basiszeichen.
const collator = new Intl.Collator('en-US', { sensitivity: 'base' });
console.log(collator.compare('a', 'A'));
// Output: 0 (equal)
console.log(collator.compare('a', 'á'));
// Output: 0 (equal)
console.log(collator.compare('a', 'b'));
// Output: -1 (different base characters)
Dieser Modus behandelt a, A und á als identisch, da sie dasselbe Basiszeichen haben.
Die Sensitivität accent berücksichtigt Akzente, ignoriert aber die Groß-/Kleinschreibung.
const collator = new Intl.Collator('en-US', { sensitivity: 'accent' });
console.log(collator.compare('a', 'A'));
// Output: 0 (equal, case ignored)
console.log(collator.compare('a', 'á'));
// Output: -1 (different, accent matters)
Die Sensitivität case berücksichtigt die Groß-/Kleinschreibung, ignoriert aber Akzente.
const collator = new Intl.Collator('en-US', { sensitivity: 'case' });
console.log(collator.compare('a', 'A'));
// Output: -1 (different, case matters)
console.log(collator.compare('a', 'á'));
// Output: 0 (equal, accent ignored)
Die Sensitivität variant (die Standardeinstellung) berücksichtigt alle Unterschiede.
const collator = new Intl.Collator('en-US', { sensitivity: 'variant' });
console.log(collator.compare('a', 'A'));
// Output: -1 (different)
console.log(collator.compare('a', 'á'));
// Output: -1 (different)
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 von 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'];
// Default sorting (wrong order)
const defaultSorted = [...files].sort();
console.log(defaultSorted);
// Output: ['file1.txt', 'file10.txt', 'file2.txt', 'file20.txt']
// Numeric sorting (correct order)
const collator = new Intl.Collator('en-US', { numeric: true });
const numericSorted = files.sort(collator.compare);
console.log(numericSorted);
// Output: ['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, da das erste Zeichen 1 einen niedrigeren Codepunkt hat als 2.
Bei 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 mischen.
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);
// Output: ['v1.2', 'v1.3', 'v1.10', 'v1.20']
Wie man steuert, welche Groß-/Kleinschreibung zuerst kommt
Die Option caseFirst bestimmt, ob Groß- oder Kleinbuchstaben zuerst sortiert werden, wenn Zeichenketten verglichen werden, die sich nur in der Groß-/Kleinschreibung unterscheiden. Sie akzeptiert drei Werte: upper, lower oder false.
const words = ['apple', 'Apple', 'APPLE'];
// Uppercase first
const upperFirst = new Intl.Collator('en-US', { caseFirst: 'upper' });
const upperSorted = [...words].sort(upperFirst.compare);
console.log(upperSorted);
// Output: ['APPLE', 'Apple', 'apple']
// Lowercase first
const lowerFirst = new Intl.Collator('en-US', { caseFirst: 'lower' });
const lowerSorted = [...words].sort(lowerFirst.compare);
console.log(lowerSorted);
// Output: ['apple', 'Apple', 'APPLE']
// Default (locale-dependent)
const defaultCase = new Intl.Collator('en-US', { caseFirst: 'false' });
const defaultSorted = [...words].sort(defaultCase.compare);
console.log(defaultSorted);
// Output depends on locale
Der Wert false verwendet die Standard-Groß-/Kleinschreibungsreihenfolge der Locale. Die meisten Locales behandeln Zeichenketten, die sich nur in der Groß-/Kleinschreibung unterscheiden, als gleich, wenn Standard-Sensitivitätseinstellungen verwendet werden.
Diese Option hat nur dann eine Auswirkung, wenn die Option sensitivity zulässt, dass Groß-/Kleinschreibungsunterschiede eine Rolle spielen.
Wie man Interpunktion beim Sortieren ignoriert
Die Option ignorePunctuation weist den Collator an, Satzzeichen beim Vergleichen 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',
];
// Default (punctuation matters)
const defaultCollator = new Intl.Collator('en-US');
const defaultSorted = [...titles].sort(defaultCollator.compare);
console.log(defaultSorted);
// Output: ['The Old Man', 'The Old-Man', 'The Oldman']
// Ignore punctuation
const noPunctCollator = new Intl.Collator('en-US', { ignorePunctuation: true });
const noPunctSorted = [...titles].sort(noPunctCollator.compare);
console.log(noPunctSorted);
// Output: ['The Old Man', 'The Old-Man', 'The Oldman']
Wenn Interpunktion ignoriert wird, behandelt der Vergleich den Bindestrich in "Old-Man", als ob er nicht existiert, wodurch die Zeichenketten so verglichen werden, als wären sie alle "TheOldMan".
Sortierung von Benutzernamen aus verschiedenen Ländern
Beim Sortieren von Namen von Benutzern aus der ganzen Welt sollten Sie das bevorzugte Locale des Benutzers verwenden, um deren 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 Locale 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.
Sortierung mit Locale-Wechsel
Wenn Ihre Anwendung es Benutzern ermöglicht, die Sprache zu wechseln, aktualisieren Sie den Collator, wenn sich das Locale ä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);
}
// User switches to Swedish
setLocale('sv-SE');
const names = ['Åsa', 'Anna', 'Örjan'];
console.log(sortItems(names));
// Output: ['Anna', 'Åsa', 'Örjan']
// User switches to German
setLocale('de-DE');
const germanNames = ['Über', 'Uhr', 'Udo'];
console.log(sortItems(germanNames));
// Output: ['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 Performance-Unterschied 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 demselben Locale und denselben Optionen sortieren. Das einmalige Erstellen des Collators und dessen Wiederverwendung bietet eine bessere Performance.
const collator = new Intl.Collator('en-US', { sensitivity: 'base', numeric: true });
const products = [/* large array */];
const sorted = products.sort(collator.compare);
Beide Ansätze liefern dieselben Ergebnisse. Die Wahl hängt von Ihren Performance-Anforderungen und Code-Organisationspräferenzen ab.