Wenn ihr in eurem Code nur mit englischen Strings zu tun habt, dann können ihr jetzt aufhören weiter zu lesen. Es sei denn, ihr sind neugierig und wollt etwas Neues lernen, dann bleiben bitte dran. 😉
In Sprachen wie Deutsch gibt es Zeichen, die im englischen Alphabet nicht vorkommen. Manche sehen anderen Zeichen ähnlich, aber manche sind extra. Und im Deutschen haben wir nur 4 davon, andere Sprachen haben viele mehr.
Wie Strings normalerweise sortiert werden
Wenn ihre ein Array von Strings habt, gibt es verschiedene Möglichkeiten, wie sie sortiert werden. Schauen wir uns einige Beispiele an:
console.log(['c', 'b', 'a', 'B', 'A', 'C'].sort())
// (6) ['A', 'B', 'C', 'a', 'b', 'c']
Diejenigen von euch, die schon einmal Code geschrieben und mit einer einfachen Sortierfunktion ein Array von Strings sortiert haben, ist vielleicht aufgefallen, dass viele dieser Funktionen die Reihenfolge der Zeichen in der ASCII-Tabelle verwenden. Deshalb sehen wir zuerst die Großbuchstaben, gefolgt von den Kleinbuchstaben.
Das mag für Englisch genau das sein, was wir erwarten und wollen. Aber was passiert, wenn wir ein paar deutsche Umlaute in das Array einfügen:
console.log(['ä', 'b', 'a', 'B', 'Ä', 'A'].sort())
// (6) ['A', 'B', 'a', 'b', 'Ä', 'ä']
Hm, das große „Ä“ kommt nach allen anderen Kleinbuchstaben, das hätten wir selbst eher nicht so sortiert. Da die Umlaute nicht Teil der ASCII-Tabelle sind (wie die meisten Buchstaben, die nicht in „Basic Latin“ enthalten sind), verwendet JavaScript die UTF-16 Codes, um sie zu sortieren. Diese stellen jedes Zeichen mit einer Zahl dar. Der Buchstabe „Ä“ zum Beispiel hat den numerischen Wert 228, während „z“ den Wert 122 hat, und deshalb ist er wie oben vor dem „Ä“ angeordnet.
Einsatz des Intl.Collator Objekts für die natürliche Sortierung
Die Lösung für dieses Problem ist wirklich sehr einfach. Die sort()
Funktion für ein Array kann eine Callback-Funktion als Parameter übergeben bekommen. Dieser Funktion werden zwei Werte des Arrays übergeben. Sie soll eine negative Zahl zurückgeben, wenn der erste Wert kleiner ist als der zweite, eine positive Zahl, wenn das Gegenteil der Fall ist, oder 0
, wenn beide den gleichen Wert haben.
Aber wir müssen eine solche Callback-Funktion nicht für jede Sprache selbst schreiben. Stattdessen können wir das Intl.Collator
Objekt verwenden. Für unser Beispiel mit den deutschen Umlauten würde der Code wie folgt aussehen:
console.log(['ä', 'b', 'a', 'B', 'Ä', 'A'].sort(new Intl.Collator('de').compare));
// (6) ['a', 'A', 'ä', 'Ä', 'b', 'B']
Jetzt haben wir nicht nur unsere Umlaute neben den ähnlich aussehenden Buchstaben, sondern wir erhalten auch jeden Kleinbuchstaben gefolgt von seinem Großbuchstaben. Das ist auch das, was man von einer „Wörterbuchsortierung“ erwarten würde.
Direkte Verwendung der Vergleichsfunktion
Wie bereits erwähnt, erwartet die Funktion sort()
eine Callback-Funktion, der eine negative oder positive Zahl oder eine 0
zurückgibt. Und genau das tut die Funktion auch. Wir können diese Funktion direkt aufrufen (und nicht nur auf ihren Namen übergeben) und zwei Buchstaben zum Vergleich übergeben:
new Intl.Collator('de').compare('ä', 'a')
// 1
new Intl.Collator('de').compare('a', 'ä')
// -1
new Intl.Collator('de').compare('a', 'a')
// 0
Um zu zeigen, dass diese Funktion nicht einfach eine statische Liste von Zahlen für jedes Zeichen verwendet, sondern wirklich nach einer bestimmten Sprache sortiert, habe ich hier ein gutes Beispiel dafür:
new Intl.Collator('de').compare('ä', 'z')
// -1
new Intl.Collator('sv').compare('ä', 'z')
// 1
Die schwedische Sprache, die ich gerade versuche zu lernen, hat auch den Buchstaben „ä“. Er ist jedoch anders sortiert als ein deutscher Umlaut, und man findet Wörter, die mit „ä“ beginnen, in einem schwedischen Wörterbuch fast am Ende (erst danach folgen Wörter, die mit „ö“ beginnen).
Erweiterte Optionen verwenden
Es gibt einige Optionen, die ihre zusätzlich als ein Objekt an die Funktion sort()
übergeben könnt, wie in diesem Beispiel, wenn ihr die Großbuchstaben zuerst haben möchtet:
console.log(
['ä', 'b', 'a', 'B', 'Ä', 'A'].sort(
new Intl.Collator(
'de',
{
caseFirst: 'upper'
}
).compare
)
);
// (6) ['A', 'a', 'Ä', 'ä', 'B', 'b']
Weitere Optionen finden ihr auf der Dokumentationsseite oder eine vollständige Liste in der ECMA-Spezifikation. Es gibt sogar einige wilde „Sprachen“ wie"de-u-co-phonebk
„, um ein Array so anzuordnen, wie es in einem deutschen Telefonbuch stehen würde – falls ihr wisst, wovon ich rede. 😁
Bonus: Zahlenreihen sortieren
Ich möchte nicht über alle Optionen schreiben, aber eine könnte in eurem Code wirklich hilfreich sein. Wenn ihr schon einmal ein Array mit Zahlen als Strings sortiert habt, dann wissen ihr bestimmt, dass die alphanumerische Sortierung hier nicht funktioniert:
console.log(['1', '2', '11', '101', '3'].sort());
// (5) ['1', '101', '11', '2', '3']
Aber mit der Option numeric
erhält man eine Sortierung, die man erwarten würde (der erste Parameter „Sprache“ ist hier vermutlich nicht so wichtig):
console.log(
['1', '2', '11', '101', '3'].sort(
new Intl.Collator(
'en',
{
numeric: true
}
).compare
)
);
// (5) ['1', '2', '3', '11', '101']
Genau das, was wir haben wollten. 😊
Fazit
Die Sortierung von Strings nach ihrer natürlichen Reihenfolge in einer bestimmten Sprache kann wirklich einen großen Unterschied in Bezug auf die Nutzbarkeit eures Codes machen (oder sogar dazu führen, dass der Code kaputt ist, wenn falsch sortiert wird). Mit dem Intl.Collator
Objekt ist das in JavaScript auch recht einfach möglich. Andere Programmiersprachen bieten oft kein so mächtiges Werkzeug an.