Abschnitt 6 · Lektion 5
Umformen mit map
map ist die erste von drei Methoden, die zusammen den Rest dieses Abschnitts tragen. Sie formt eine Liste um: gleich viele Einträge, andere Werte.
Aus alt mach neu
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [1, 2, 3, 4];
// So ginge es von Hand: leere Liste, Schleife, push.
const vonHand = [];
for (const zahl of zahlen) {
vonHand.push(zahl * 2);
}
console.log("Von Hand:", vonHand);
// Und so mit map. Dieselbe Idee, eine Zeile.
const mitMap = zahlen.map((zahl) => zahl * 2);
console.log("Mit map:", mitMap);
// Das Original bleibt unangetastet.
console.log("Original:", zahlen);
// Der Callback bekommt auch den Index, wenn du ihn brauchst.
console.log("Nummeriert:", zahlen.map((zahl, index) => `${index + 1}: ${zahl}`)); Das erste Beispiel zeigt beide Wege nebeneinander. Von Hand sind es vier Zeilen: leere Liste anlegen, Schleife, push, fertig. Mit map ist es eine.
map ruft die übergebene Funktion einmal je Eintrag auf und sammelt ihre Rückgaben in einer neuen Liste. Das Original bleibt stehen, ganz wie slice aus Lektion 6.2.
Das kannst du in der Eingabezeile der Console in zwei Zeilen nachsehen, ohne die Datei anzufassen: zahlen.map((zahl) => zahl * 3) antwortet mit einer neuen Liste, und zahlen allein getippt antwortet danach mit der alten. Merk dir den Handgriff, denn er beantwortet für jede Methode dieses Abschnitts dieselbe Frage. In Lektion 6.8 kommt das Gegenbeispiel.
Der Callback bekommt dieselben drei Argumente wie bei forEach: Wert, Index und die ganze Liste. Meistens braucht man nur den ersten.
Der Gewinn ist nicht die gesparte Zeile, sondern der Name. map sagt beim Lesen sofort: Hier kommt aus jedem Eintrag ein neuer. Bei der Schleife musst du das erst aus drei Zeilen zusammensetzen.
Gleich lang, immer
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [1, 2, 3, 4, 5];
// Jeder Eintrag wird umgeformt, keiner fällt weg. Die neue Liste
// hat immer genauso viele Einträge wie die alte.
const verdoppelt = zahlen.map((zahl) => zahl * 2);
console.log(zahlen.length, verdoppelt.length);
// Auch dann, wenn die Bedingung im Callback steht: heraus kommt
// kein kürzeres Ergebnis, sondern eins mit Lücken aus false.
const nurGerade = zahlen.map((zahl) => zahl % 2 === 0);
console.log("Mit Bedingung:", nurGerade);
// Zum Aussortieren gibt es filter, das ist die nächste Lektion.
// Der Typ darf sich ändern. Aus Zahlen werden Texte, und die
// Liste ist trotzdem gleich lang.
console.log("Als Text:", zahlen.map((zahl) => `Nummer ${zahl}`)); Die neue Liste ist immer genauso lang wie die alte. Jeder Eintrag geht hinein, jeder kommt heraus, nur eben verändert.
Das ist der wichtigste Unterschied zu filter aus der nächsten Lektion, und es ist der häufigste Denkfehler: Eine Bedingung im Callback sortiert nichts aus. Sie sorgt nur dafür, dass in der neuen Liste true und false stehen.
Was sich ändern darf, ist der Typ. Aus Zahlen werden Texte, aus Texten Zahlen, aus allem Objekte. Die Länge bleibt trotzdem.
Der Fehler, den jeder einmal macht
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahlen = [1, 2, 3];
// Geschweifte Klammern gesetzt, return vergessen: überall undefined.
const kaputt = zahlen.map((zahl) => {
zahl * 2;
});
console.log("Ohne return:", kaputt);
// Mit return stimmt es.
const heil = zahlen.map((zahl) => {
return zahl * 2;
});
console.log("Mit return:", heil);
// Und ohne geschweifte Klammern braucht es keins.
console.log("Kurzform:", zahlen.map((zahl) => zahl * 2));
// Die Länge stimmt in allen drei Fällen. Genau deshalb fällt der
// Fehler nicht an der Länge auf, sondern erst am Inhalt.
console.log(kaputt.length, heil.length); Der Callback muss etwas zurückgeben, sonst steht in der neuen Liste überall undefined.
Das passiert genau dann, wenn du geschweifte Klammern setzt und das return vergisst, also der Fall aus Lektion 5.4. Bei einer Arrow Function ohne Klammern kann es nicht passieren.
Und es fällt nicht an der Länge auf. Die stimmt ja, die neue Liste hat genauso viele Einträge wie die alte. Nur steht in jedem undefined. Wenn also eine Liste plötzlich voller undefined ist, schau als Erstes in den map-Callback.
Der praktische Fall
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const preiseInCent = [199, 2450, 99];
// Ein längerer Callback braucht Klammern und return, dafür passt
// eine Zwischenrechnung hinein.
const alsEuro = preiseInCent.map((cent) => {
const euro = Math.floor(cent / 100);
const rest = String(cent % 100).padStart(2, "0");
return `${euro},${rest} Euro`;
});
console.log(alsEuro);
// Steht die Umformung schon als Funktion da, reicht ihr Name.
// Keine Klammern dahinter, map ruft sie ja selbst auf.
function verdoppeln(zahl) {
return zahl * 2;
}
console.log([1, 2, 3].map(verdoppeln)); So sieht map in echtem Code aus: eine Liste roher Werte hinein, eine Liste fertiger Texte heraus.
Ist der Callback länger als ein Ausdruck, kommen geschweifte Klammern und return dazu, und dann passt eine Zwischenrechnung hinein. Das ist völlig üblich und immer noch besser lesbar als die Schleife von Hand.
Steht die Umformung schon als eigene Funktion da, reicht ihr Name ohne Klammern. map ruft sie selbst auf, du reichst sie nur weiter. Das ist der Unterschied aus Lektion 5.7, und hier ist er wirklich nützlich.
Ab Abschnitt 11 wirst du diese Form ständig sehen, dann allerdings mit Markup statt Text: aus einer Liste von Daten wird eine Liste von Elementen, eines je Eintrag. Das Muster ist genau dasselbe.
Zum Mitnehmen
map gibt eine neue Liste zurück und lässt die alte stehen. Sie ist immer genauso lang wie die alte, denn jeder Eintrag wird umgeformt, keiner fällt weg.
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.