Array.prototype.map() erzeugt ein neues Array, indem auf jedes Element des Originals eine Transformations-Funktion angewandt wird. Anders als forEach (das nur einen Side-Effect ausführt) liefert map ein Ergebnis, das man weiterverarbeiten kann. Das Original bleibt unverändert — map ist die wichtigste immutable Array-Operation in JavaScript. In Kombination mit filter und reduce ergibt sich der typische funktionale Pipeline-Stil. Der Callback bekommt (element, index, array) — die zwei zusätzlichen Argumente werden nur bei Bedarf genutzt.
Signatur & Grundform
arr.map(callback: (el: T, i: number, arr: T[]) => U, thisArg?): U[]const zahlen = [1, 2, 3, 4];
const verdoppelt = zahlen.map(x => x * 2);
console.log(zahlen); // [1, 2, 3, 4] — unverändert
console.log(verdoppelt); // [2, 4, 6, 8][ 1, 2, 3, 4 ]
[ 2, 4, 6, 8 ]map ist eine der ersten Methoden, die jeder JavaScript-Lernende verinnerlicht. Sie steht im Zentrum des funktionalen Stils.
Callback-Argumente: (element, index, array)
Der Callback bekommt drei Argumente — die meisten nutzen nur das erste.
const items = ['a', 'b', 'c'];
// Nur das Element
console.log(items.map(x => x.toUpperCase()));
// Element + Index
console.log(items.map((x, i) => `${i}: ${x}`));
// Alle drei
const annotated = items.map((x, i, arr) => ({
wert: x,
index: i,
isLast: i === arr.length - 1,
}));
console.log(annotated);[ 'A', 'B', 'C' ]
[ '0: a', '1: b', '2: c' ]
[
{ wert: 'a', index: 0, isLast: false },
{ wert: 'b', index: 1, isLast: false },
{ wert: 'c', index: 2, isLast: true }
]Object-Transformation
Sehr verbreitet: Objekte auf Untermengen oder neue Strukturen mappen.
const users = [
{ id: 1, name: 'Anna', email: 'anna@x.de' },
{ id: 2, name: 'Bob', email: 'bob@x.de' },
];
// Namen extrahieren
const namen = users.map(u => u.name);
console.log(namen);
// Object-Shape umbauen
const summary = users.map(u => ({
label: `${u.name} (#${u.id})`,
kontakt: u.email,
}));
console.log(summary);[ 'Anna', 'Bob' ]
[
{ label: 'Anna (#1)', kontakt: 'anna@x.de' },
{ label: 'Bob (#2)', kontakt: 'bob@x.de' }
]Pipelines mit filter und reduce
Der idiomatische Stil: erst filtern, dann mappen, dann ggf. aggregieren.
const products = [
{ name: 'Buch', preis: 15, verfuegbar: true },
{ name: 'Lampe', preis: 30, verfuegbar: false },
{ name: 'Stuhl', preis: 100, verfuegbar: true },
{ name: 'Tisch', preis: 200, verfuegbar: true },
];
const summeVerfuegbar = products
.filter(p => p.verfuegbar)
.map(p => p.preis)
.reduce((acc, p) => acc + p, 0);
console.log(summeVerfuegbar); // 15 + 100 + 200 = 315315Jede Methode liefert ein neues Array, kettenbar. Performance: drei Iterationen vs. einer mit klassischem for — meistens unerheblich, bei großen Daten messbar.
map vs. forEach
Beide laufen über das Array. Unterschied: map gibt ein neues Array zurück, forEach nicht. Wenn der Rückgabewert gebraucht wird, ist map die richtige Wahl.
const arr = [1, 2, 3];
// map: liefert neues Array
const a = arr.map(x => x * 2);
console.log(a); // [2, 4, 6]
// forEach: kein Rückgabewert (undefined)
const b = arr.forEach(x => x * 2);
console.log(b); // undefined
// Anti-Pattern: forEach mit Push
const c = [];
arr.forEach(x => c.push(x * 2));
console.log(c); // [2, 4, 6] — funktioniert, aber map ist klarer[ 2, 4, 6 ]
undefined
[ 2, 4, 6 ]Wenn der Sinn eine Transformation ist: map. Wenn der Sinn ein Side-Effect ist (Logging, DOM-Manipulation): forEach. Niemals map ohne den Rückgabewert zu nutzen — Linter (ESLint array-callback-return) warnen davor.
flatMap — Map + Flatten in einem Schritt
Wenn der Callback ein Array zurückgibt und das Ergebnis flach werden soll, gibt es seit ES2019 flatMap.
const woerter = ['Hallo Welt', 'wie geht es dir'];
// map: liefert verschachteltes Array
const verschachtelt = woerter.map(s => s.split(' '));
console.log(verschachtelt); // [['Hallo', 'Welt'], ['wie', 'geht', 'es', 'dir']]
// flatMap: flach
const flach = woerter.flatMap(s => s.split(' '));
console.log(flach);[ [ 'Hallo', 'Welt' ], [ 'wie', 'geht', 'es', 'dir' ] ]
[ 'Hallo', 'Welt', 'wie', 'geht', 'es', 'dir' ]Äquivalent zu arr.map(fn).flat(1), aber effizienter (eine Iteration statt zwei).
map mit async — die Promise-Falle
map ist synchron. Wenn der Callback async ist, liefert map ein Array von Promises — keine fertigen Werte.
async function ladeUser(id) {
return { id, name: `User-${id}` };
}
// FALSCH: liefert Array<Promise>
const arr1 = [1, 2, 3].map(id => ladeUser(id));
console.log(arr1[0]); // Promise {...}, nicht das User-Objekt
// RICHTIG: Promise.all + map
(async () => {
const users = await Promise.all([1, 2, 3].map(ladeUser));
console.log(users);
})();Promise { { id: 1, name: 'User-1' } }
[
{ id: 1, name: 'User-1' },
{ id: 2, name: 'User-2' },
{ id: 3, name: 'User-3' }
]Promise.all parallelisiert die Calls. Wenn sequenziell nötig: for-of mit await im Body.
Sparse Arrays — Löcher werden übersprungen
map überspringt Löcher in sparse Arrays — der Callback wird für sie nicht aufgerufen. Die Löcher bleiben im Ergebnis-Array Löcher.
const sparse = [1, , 3];
const verdoppelt = sparse.map(x => x * 2);
console.log(verdoppelt); // [2, <1 empty item>, 6]
console.log(1 in verdoppelt); // false — Loch erhalten[ 2, <1 empty item>, 6 ]
falseIn modernem Code irrelevant, weil sparse Arrays selten sind. Bei Legacy-Code mit new Array(n) aber wichtig zu wissen.
Besonderheiten
map ist immer immutable — das Original wird nie verändert
Anders als sort, reverse, push erzeugt map grundsätzlich ein neues Array. Daher ist es die Standard-Methode für State-Updates in Redux/React.
Callback bekommt drei Argumente: (el, index, array)
Häufig wird nur das erste genutzt. Index ist praktisch für „erstes/letztes Element"-Logik oder für ID-Generierung. Das dritte (array) ist selten relevant.
map mit async Callback liefert Promises, nicht Werte
Klassische Falle: arr.map(async x => await foo(x)) ergibt ein Array von Promises. Für die fertigen Werte: await Promise.all(arr.map(async ...)). Sequenziell: for-of mit await.
map mit Index für ID-Generierung statt manueller for-Schleife
arr.map((x, i) => ({ id: i, value: x })) erzeugt eine Sammlung mit Index-IDs. Lesbarer als eine For-Schleife mit manuellem Push.
Rückgabe undefined aus Callback erzeugt undefined im Ergebnis
[1, 2, 3].map(x => { if (x > 1) return x * 2; }) liefert [undefined, 4, 6] — der erste Eintrag fehlt nicht, er ist undefined. Wer Elemente weglassen will: filter davor oder dahinter.
ESLint array-callback-return warnt, wenn map kein return hat
arr.map(x => { console.log(x); }) ist meist ein Bug — map ohne Rückgabewert ist sinnlos. Linter weisen darauf hin. Wenn nur Side-Effect gemeint: forEach.
flatMap (ES2019) für Map + Flatten in einem Pass
arr.flatMap(fn) ist äquivalent zu arr.map(fn).flat(1), aber effizienter (eine Iteration). Praktisch wenn der Callback ein Array zurückgibt und das Ergebnis flach werden soll.
Performance: einzelne map oft schneller als Pipeline
arr.filter(f1).map(f2).reduce(f3) macht drei Iterationen. Eine kombinierte for-of-Schleife wäre eine. Bei großen Arrays (>100k) messbar, bei normalem Code irrelevant. Lazy-Evaluation via Iterator Helpers (ES2025) löst das systematisch.
Weiterführende Ressourcen
Externe Quellen
- Array.prototype.map() – MDN
- Array.prototype.flatMap() – MDN
- Array.prototype.map – ECMAScript Spec
- Promise.all() – MDN