Abschnitt 6 · Lektion 9
Synthese: die Bestenliste
Der Abschluss dieses Abschnitts bringt zusammen, was einzeln schon dastand. Filtern, sortieren, umformen und zusammenfassen sind vier Werkzeuge, und in echtem Code stehen sie fast immer hintereinander.
Warum das Verketten funktioniert
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [3, 12, 5, 20, 1, 9];
// In Einzelschritten, mit einem Namen für jede Stufe.
const grosse = zahlen.filter((zahl) => zahl > 4);
const sortiert = grosse.slice().sort((a, b) => b - a);
const beschriftet = sortiert.map((zahl) => `Wert: ${zahl}`);
console.log("Einzeln:", beschriftet);
// Dasselbe als Kette. Jede Stufe gibt eine Liste zurück, deshalb
// darf die nächste direkt dahinter stehen.
const kette = zahlen
.filter((zahl) => zahl > 4)
.slice()
.sort((a, b) => b - a)
.map((zahl) => `Wert: ${zahl}`);
console.log("Als Kette:", kette); Der Grund ist einfach: Jede dieser Methoden gibt wieder eine Liste zurück. Und auf einer Liste darfst du die nächste Methode aufrufen.
Damit kannst du die Zwischennamen weglassen und die Stufen direkt aneinanderhängen. Der Punkt am Zeilenanfang ist dabei kein Sonderzeichen, sondern derselbe Punkt wie immer, nur umgebrochen.
Diese Schreibweise mit einer Stufe je Zeile ist die übliche. Sie liest sich von oben nach unten wie ein Ablauf: erst das, dann das, dann das.
Eine Ausnahme ist reduce. Es gibt keine Liste zurück, sondern einen einzelnen Wert. Deshalb steht es immer am Ende einer Kette, nie mittendrin.
Die Reihenfolge
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [3, 12, 5, 20, 1, 9];
// Erst aussortieren, dann umformen: map fasst nur noch drei an.
const richtig = zahlen
.filter((zahl) => zahl > 4)
.map((zahl) => {
console.log("map arbeitet an", zahl);
return zahl * 2;
});
console.log("Ergebnis:", richtig);
console.log("---");
// Andersherum: map fasst alle sechs an, filter wirft danach
// die Hälfte weg. Dasselbe Ergebnis, doppelte Arbeit.
const teurer = zahlen
.map((zahl) => {
console.log("map arbeitet an", zahl);
return zahl * 2;
})
.filter((zahl) => zahl > 8);
console.log("Ergebnis:", teurer); Die übliche Reihenfolge ist erst filter, dann sort, dann map, und dahinter liegt eine simple Überlegung: Jede Stufe gibt weniger Einträge weiter als die vorige.
Filtert man zuerst, formt map nur noch die drei übrigen um. Formt man zuerst um, fasst map alle sechs an und filter wirft die Hälfte danach weg. Das Ergebnis ist dasselbe, die Arbeit ist doppelt.
Im Beispiel siehst du das an den Ausgaben aus dem map-Callback: einmal drei Zeilen, einmal sechs.
Bei sechs Einträgen ist das egal. Es ist trotzdem die richtige Gewohnheit, und es liest sich auch besser: erst aussortieren, dann herrichten.
Eine Warnung zum Sortieren in einer Kette: sort verändert die Liste, auf der es steht. Steht davor ein filter, ist das harmlos, denn filter hat ja schon eine neue Liste erzeugt. Steht sort direkt auf dem Original, sortiert es das Original um.
Wann eine Kette zu lang ist
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const ergebnisse = [
["Ben", 91],
["Tom", 52],
["Ida", 98],
];
// Fünf Stufen am Stück. Läuft, und beim Suchen eines Fehlers
// weiß man nicht, welche Stufe schuld ist.
console.log(
ergebnisse
.filter((e) => e[1] >= 60)
.slice()
.sort((a, b) => b[1] - a[1])
.slice(0, 2)
.map((e, i) => `${i + 1}. ${e[0]}`)
);
// Mit Zwischenschritten. Jeder hat einen Namen, jeder ist
// einzeln ausgebbar.
const bestanden = ergebnisse.filter((e) => e[1] >= 60);
const nachPunkten = bestanden.slice().sort((a, b) => b[1] - a[1]);
const besteZwei = nachPunkten.slice(0, 2);
console.log("Bestanden:", bestanden);
console.log("Sortiert:", nachPunkten);
console.log("Fertig:", besteZwei.map((e, i) => `${i + 1}. ${e[0]}`)); Eine Kette darf beliebig lang werden, und ab einer bestimmten Länge sollte sie es nicht mehr.
Der Test ist derselbe wie bei den Funktionen in Lektion 5.8: Kannst du in einem Satz sagen, was hier passiert? Wenn nicht, zerleg es in benannte Zwischenschritte.
Der Gewinn ist beim Suchen eines Fehlers am größten. Stimmt das Ergebnis nicht, gibst du jeden Zwischenschritt einzeln aus und siehst sofort, welche Stufe schuld ist. In einer Kette am Stück geht das nicht ohne Umbau.
Als Faustregel: Bis drei Stufen als Kette, ab vier lohnt ein Zwischenname. Und wenn eine Stufe selbst kompliziert wird, bekommt sie eine eigene Funktion.
Lesbar, nicht schneller
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [3, 12, 5, 20, 1, 9];
// Die Kette läuft dreimal über die Daten: einmal je Stufe.
const mitKette = zahlen
.filter((zahl) => zahl > 4)
.map((zahl) => zahl * 2)
.reduce((summe, zahl) => summe + zahl, 0);
console.log("Mit Kette:", mitKette);
// Die Schleife läuft einmal und ist damit sparsamer. Und sie
// ist schwerer zu lesen, weil drei Gedanken darin stecken.
let mitSchleife = 0;
for (const zahl of zahlen) {
if (zahl > 4) {
mitSchleife = mitSchleife + zahl * 2;
}
}
console.log("Mit Schleife:", mitSchleife);
// Bei sechs Einträgen ist der Unterschied nicht messbar. Bei
// Millionen wäre er es, und dann rechnet man nach, statt zu raten. Zum Schluss eine Klarstellung, weil das oft falsch erzählt wird: Eine Kette ist nicht schneller als eine Schleife. Sie ist meistens langsamer, denn jede Stufe läuft einmal über die Daten und legt eine neue Liste an, während die Schleife einmal läuft.
Der Grund für die Kette ist die Lesbarkeit. In der Schleife stecken drei Gedanken in vier Zeilen, und man muss sie auseinandersortieren. In der Kette steht jeder Gedanke in einer eigenen Zeile mit einem Namen davor.
Bei den Listengrößen, mit denen du auf einer Webseite zu tun hast, ist der Unterschied nicht messbar. Und wenn er es einmal ist, misst man nach, statt zu raten. Das ist derselbe Gedanke wie bei den verschachtelten Schleifen in Lektion 4.4.
Zum Mitnehmen
Jede dieser Methoden gibt wieder eine Liste zurück. Deshalb darf die nächste direkt dahinter stehen, und deshalb liest sich eine Kette von oben nach unten wie ein Ablauf.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.