Abschnitt 6 · Lektion 6
Filtern mit filter
filter ist die zweite der drei Methoden. Sie formt nichts um, sie lässt weg.
Behalten oder nicht
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [3, 12, 5, 20, 1, 9];
// Der Callback entscheidet für jeden Eintrag: behalten oder nicht.
console.log("Über 10:", zahlen.filter((zahl) => zahl > 10));
console.log("Gerade:", zahlen.filter((zahl) => zahl % 2 === 0));
// Das Original bleibt stehen, genau wie bei map.
console.log("Original:", zahlen);
// Die Reihenfolge bleibt auch. filter sortiert nichts um, es
// lässt nur weg.
console.log("Unter 10:", zahlen.filter((zahl) => zahl < 10)); filter ruft die übergebene Funktion einmal je Eintrag auf und behält jeden, für den sie einen wahren Wert liefert. Die anderen fallen weg.
Der Aufbau ist derselbe wie bei map: eine Funktion je Eintrag, eine neue Liste zurück, das Original bleibt stehen. Nur die Bedeutung des Rückgabewerts ist eine andere. Bei map ist er der neue Eintrag, bei filter die Entscheidung.
Auch die Reihenfolge bleibt. filter sortiert nichts um, es lässt nur Lücken zusammenrücken.
Beachte, dass wirklich die Falsy-Regeln aus Lektion 3.3 gelten. Der Callback muss kein echtes true liefern, es reicht ein truthy Wert. Das ist bequem und gelegentlich verwirrend: Ein Callback, der versehentlich einen leeren Text zurückgibt, sortiert damit aus.
Leer ist ein gültiges Ergebnis
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [3, 12, 5];
// Trifft nichts zu, kommt eine leere Liste heraus. Kein undefined
// und kein null, sondern wirklich eine Liste ohne Einträge.
const keine = zahlen.filter((zahl) => zahl > 100);
console.log("Nichts gefunden:", keine, "Länge:", keine.length);
// Deshalb kann man immer weiterarbeiten, ohne vorher zu prüfen.
console.log("Verdoppelt:", keine.map((zahl) => zahl * 2));
// Und deshalb fragt man nach der Länge, nicht nach dem Wert.
// Eine leere Liste ist truthy, wie du aus Lektion 3.3 weißt.
console.log("Als Bedingung falsch geprüft:", keine ? "Treffer" : "nichts");
console.log("Richtig geprüft:", keine.length > 0 ? "Treffer" : "nichts"); Trifft die Bedingung auf keinen Eintrag zu, kommt eine leere Liste heraus. Kein undefined, kein null, sondern eine Liste ohne Einträge.
Das ist praktisch, denn du kannst ohne Prüfung weiterarbeiten. Ein map auf eine leere Liste ergibt wieder eine leere Liste, eine Schleife darüber läuft null Mal, und nichts davon bricht ab.
Frag nach der Länge, nicht nach dem Wert. Eine leere Liste ist truthy, wie jedes Objekt. if (treffer) besteht deshalb auch dann, wenn nichts gefunden wurde. Richtig ist if (treffer.length > 0).
filter oder find
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [3, 12, 5, 20, 1];
// Dieselbe Bedingung, zwei verschiedene Fragen.
console.log("filter, alle Treffer:", zahlen.filter((zahl) => zahl > 10));
console.log("find, der erste:", zahlen.find((zahl) => zahl > 10));
// Und ohne Treffer entsprechend verschieden.
console.log("filter ohne Treffer:", zahlen.filter((zahl) => zahl > 100));
console.log("find ohne Treffer:", zahlen.find((zahl) => zahl > 100));
// Der Fehler, der daraus entsteht: mit dem Ergebnis von filter
// rechnen, als wäre es ein einzelner Wert.
console.log("Ein Treffer plus eins:", zahlen.find((zahl) => zahl > 10) + 1);
console.log("Alle Treffer plus eins:", zahlen.filter((zahl) => zahl > 10) + 1); Dieselbe Bedingung, zwei verschiedene Fragen, und die Antworten haben verschiedene Typen.
filter liefert alle Treffer als Liste. Auch wenn es nur einer ist, liegt er in einer Liste.
find liefert den ersten Treffer als Wert. Ohne Treffer undefined.
Daraus folgt die Auswahl: Willst du einen bestimmten Eintrag, nimm find. Willst du alle, die passen, nimm filter. Der übliche Fehler ist, mit dem Ergebnis von filter weiterzurechnen, als wäre es ein einzelner Wert. Heraus kommt dann eine Zeichenkette, weil JavaScript die Liste in Text umwandelt, und die Ursache ist zwanzig Zeilen weiter oben.
Die Standardkombination
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> // Jeder Eintrag ist selbst eine kleine Liste: vorn der Name,
// dahinter der Preis in Cent.
const artikel = [
["Buch", 1990],
["Stift", 250],
["Tasche", 4900],
["Radierer", 90],
];
// filter gibt wieder eine Liste zurück, deshalb darf map direkt
// dahinter stehen.
const teureNamen = artikel
.filter((eintrag) => eintrag[1] > 500)
.map((eintrag) => eintrag[0]);
console.log("Teurer als 5 Euro:", teureNamen);
// Andersherum ginge es auch, wäre aber mehr Arbeit: map liefe
// über alle vier, filter danach ebenfalls.
console.log("Anzahl:", teureNamen.length); filter gibt eine Liste zurück, und auf einer Liste kannst du wieder map aufrufen. Deshalb stehen die beiden fast immer hintereinander, und diese Kette ist eine der häufigsten Zeilen in echtem JavaScript.
Die übliche Reihenfolge ist erst filter, dann map. Der Grund ist Arbeit: Jede Stufe gibt weniger Einträge weiter, also formt map nur noch das um, was übrig geblieben ist. Umgekehrt würde map alles anfassen und filter danach die Hälfte wegwerfen.
Bei vier Einträgen ist das egal. Die Reihenfolge ist trotzdem die richtige Gewohnheit, und sie liest sich auch besser: erst aussortieren, dann herrichten.
Lektion 6.9 baut daraus eine längere Kette. Vorher fehlt noch die dritte Methode, die aus einer Liste einen einzelnen Wert macht.
Zum Mitnehmen
filter behält jeden Eintrag, für den die Bedingung einen wahren Wert liefert. Die neue Liste ist deshalb höchstens so lang wie die alte und darf leer sein.
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.