map, filter, reduce und die ES2023-Neuheiten
Arrays sind die meistgenutzte Datenstruktur in JavaScript – aber nur wenige Entwickler kennen den vollen Umfang der modernen Array-Methoden. Von flatMap über findLast bis zu den nicht-mutierenden ES2023-Methoden toSorted und toReversed: dieser Artikel zeigt, welche Methode wann richtig ist und wie man typische Performance-Fallen vermeidet.
Inhaltsverzeichnis
- 1. Das funktionale Grundprinzip moderner Array-Methoden
- 2. map: Transformation ohne Mutation
- 3. filter, find, findLast und findIndex
- 4. reduce: Mächtiger Akkumulator – richtig eingesetzt
- 5. flat und flatMap für verschachtelte Arrays
- 6. ES2023-Neuheiten: toSorted, toReversed, toSpliced, with
- 7. Methoden verketten: Lesbarkeit vs. Performance
- 8. Object.groupBy und Map.groupBy (ES2024)
- 9. Array-Methoden im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Das funktionale Grundprinzip moderner Array-Methoden
Moderne Array-Methoden in JavaScript sind auf ein zentrales Prinzip ausgelegt: Keine Mutation des Original-Arrays. Methoden wie map, filter und reduce geben neue Arrays oder Werte zurück, ohne das ursprüngliche Array zu verändern. Das ist kein Selbstzweck – es ist die Voraussetzung für vorhersagbares Verhalten in React-State, Redux-Reducern und anderen reaktiven Systemen, bei denen Referenzgleichheit eine Rolle spielt. Wenn eine Funktion dieselbe Eingabe immer zur selben Ausgabe transformiert und keine Seiteneffekte hat, ist sie referenziell transparent – das macht Tests einfacher und Debugging schneller.
Der Unterschied zu imperativen for-Schleifen ist nicht nur stilistisch. Array-Methoden drücken die Absicht des Codes aus, nicht den Mechanismus: prices.map(p => p * 1.19) sagt klar "transformiere jeden Preis mit Steuer", während eine äquivalente for-Schleife den Leser zwingt, den Mechanismus zu analysieren, bevor die Absicht klar wird. Für größere Codebasen ist diese Ausdrucksstärke ein messbarer Wartbarkeitsvorteil – Code wird öfter gelesen als geschrieben, und Array-Methoden machen das Lesen effizienter.
2. map: Transformation ohne Mutation
Array.prototype.map ist die wichtigste Array-Methode für Datentransformationen. Sie nimmt eine Callback-Funktion, ruft sie für jedes Element auf und gibt ein neues Array mit den Rückgabewerten zurück. Die Länge des Ergebnisarrays ist immer identisch mit der Länge des Eingabearrays. Das ist der entscheidende Unterschied zu filter. Ein häufiger Fehler: map verwenden, wenn eigentlich nur Seiteneffekte gewünscht sind – für das Iterieren ohne Rückgabe ist forEach die semantisch korrekte Wahl, auch wenn map technisch funktioniert.
In der Praxis werden Array-Methoden wie map häufig für API-Response-Transformationen eingesetzt: Ein Objekt aus einer REST-API in ein View-Modell umwandeln, Datumsstrings parsen, Feldnamen umbenennen oder berechnete Felder ergänzen. Für solche Transformationen ist es gute Praxis, eine reine Transformationsfunktion zu extrahieren und sie an map zu übergeben – statt eine komplexe Arrow-Funktion direkt im map-Aufruf zu schreiben. Das macht die Transformationsfunktion unabhängig testbar und den map-Aufruf lesbar.
// map: transform each element into a new value
const products = [
{ id: 1, name: "Laptop", price: 999, taxRate: 0.19 },
{ id: 2, name: "Mouse", price: 29, taxRate: 0.19 },
{ id: 3, name: "Book", price: 39, taxRate: 0.07 },
];
// Pure transformation function — independently testable
const addGrossPrice = (product) => ({
...product,
grossPrice: +(product.price * (1 + product.taxRate)).toFixed(2),
label: `${product.name} (${(product.taxRate * 100).toFixed(0)}% MwSt.)`,
});
const enriched = products.map(addGrossPrice);
// [{ id:1, name:"Laptop", price:999, taxRate:0.19, grossPrice:1188.81, label:"Laptop (19% MwSt.)" }, ...]
// map with index (second callback param)
const numbered = products.map((p, i) => `${i + 1}. ${p.name}`);
// ["1. Laptop", "2. Mouse", "3. Book"]
// map on DOM NodeList — NodeList is not an Array, convert first
const headings = [...document.querySelectorAll("h2")].map(h => h.textContent);
// WRONG: using map for side effects only — use forEach instead
products.map(p => console.log(p.name)); // SC equivalent: no return value used
// RIGHT: forEach for side effects
products.forEach(p => console.log(p.name));
3. filter, find, findLast und findIndex
filter gibt ein neues Array zurück, das nur die Elemente enthält, für die die Callback-Funktion true zurückgibt. Im Unterschied zu map kann das Ergebnis-Array kürzer als das Eingabe-Array sein. find gibt das erste Element zurück, das der Bedingung entspricht – oder undefined, wenn kein Element passt. Es ist effizienter als filter, wenn nur ein einziges Element benötigt wird, weil find die Iteration beim ersten Treffer abbricht. Diese Unterscheidung ist wichtig für Performance bei großen Arrays.
Mit ES2023 kamen findLast und findLastIndex hinzu – sie iterieren das Array von hinten und geben das letzte passende Element bzw. seinen Index zurück. Das war zuvor nur mit einer Kombination aus reverse() und find() möglich, was das Original-Array mutiert hätte. findLast macht diese Array-Methode nicht-mutierend verfügbar. Typischer Anwendungsfall: Das neueste Ereignis in einem chronologisch sortierten Log-Array suchen, ohne das Array umzukehren.
4. reduce: Mächtiger Akkumulator – richtig eingesetzt
reduce ist die vielseitigste Array-Methode, aber auch die am häufigsten falsch eingesetzte. reduce nimmt einen Akkumulator und ein aktuelles Element, und der Callback gibt den neuen Akkumulator zurück. Am Ende gibt reduce den finalen Akkumulator zurück. Das macht reduce in der Lage, Arrays zu Zahlen (Summe), Strings, Objekten oder anderen Arrays zu transformieren. Aber: Wenn reduce das Ergebnis von filter und map kombinieren soll, ist es oft besser lesbar, filter und map explizit zu verketten – der Zweck ist dann sofort klar.
Ein klassischer, legitimer Einsatz von reduce ist das Gruppieren: Ein Array von Objekten in ein Objekt umwandeln, das die Elemente nach einem Schlüssel gruppiert. Vor ES2024 war das der idiomatische Weg für dieses Problem. Mit Object.groupBy (ES2024) gibt es jetzt eine dedizierte Methode dafür. Für den Fall, dass reduce keinen Initialwert erhält und das Array leer ist, wirft JavaScript einen TypeError – daher immer einen Initialwert als zweites Argument übergeben, außer wenn man sicher ist, dass das Array mindestens ein Element hat.
// reduce: accumulate an array into any output shape
const orders = [
{ category: "electronics", total: 299 },
{ category: "books", total: 45 },
{ category: "electronics", total: 149 },
{ category: "books", total: 22 },
{ category: "clothing", total: 89 },
];
// Sum: array → number
const grandTotal = orders.reduce((sum, o) => sum + o.total, 0); // 604
// Max total
const maxOrder = orders.reduce(
(max, o) => (o.total > max.total ? o : max),
orders[0]
); // { category: "electronics", total: 299 }
// Group by category: array → object (pre-ES2024 idiom)
const grouped = orders.reduce((acc, order) => {
(acc[order.category] ??= []).push(order); // nullish assignment
return acc;
}, {});
// { electronics: [...], books: [...], clothing: [...] }
// ES2024: Object.groupBy — cleaner, dedicated API
const groupedModern = Object.groupBy(orders, (o) => o.category);
// Category totals: array → object
const totals = orders.reduce((acc, o) => {
acc[o.category] = (acc[o.category] ?? 0) + o.total;
return acc;
}, {});
// { electronics: 448, books: 67, clothing: 89 }
// WRONG: using reduce where filter+map is clearer
const result = orders.reduce((acc, o) => {
if (o.total > 100) acc.push(o.category.toUpperCase());
return acc;
}, []);
// RIGHT: explicit pipeline is more readable
const resultClear = orders
.filter(o => o.total > 100)
.map(o => o.category.toUpperCase());
5. flat und flatMap für verschachtelte Arrays
Array.prototype.flat gibt ein neues Array zurück, in dem alle verschachtelten Sub-Arrays bis zur angegebenen Tiefe in ein einzelnes Array "geflacht" werden. flat()` ohne Argument flacht eine Ebene, flat(Infinity) flacht beliebig tief. Diese Array-Methode ist besonders nützlich, wenn API-Antworten verschachtelte Arrays liefern oder wenn ein map-Aufruf für jedes Element ein Array produziert. Genau für letzteren Fall gibt es flatMap: Es ist äquivalent zu .map(...).flat(1), aber in einer einzigen Iteration effizienter.
Ein häufiger Anwendungsfall für flatMap: Jedes Element einer Liste in mehrere Elemente umwandeln – z.B. eine Liste von Sätzen in eine Liste aller Wörter aufteilen. Mit map allein würde man ein Array von Arrays erhalten; mit flatMap erhält man das flache Ergebnis direkt. Eine weitere wichtige Eigenschaft: flatMap kann auch als Filteroperation eingesetzt werden, indem der Callback für Elemente, die ausgeschlossen werden sollen, ein leeres Array zurückgibt – eine kompakte Alternative zu filter + map in einem Schritt.
6. ES2023-Neuheiten: toSorted, toReversed, toSpliced und with
ES2023 hat vier nicht-mutierende Varianten klassischer mutativer Array-Methoden eingeführt: toSorted, toReversed, toSpliced und with. Das Problem mit den Originalen: sort(), reverse() und splice() mutieren das Array in-place. Wer in React-State oder Redux ein Array sortieren will, muss bisher umständlich mit [...array].sort(...) eine Kopie erstellen. Mit toSorted gibt es jetzt eine Array-Methode, die eine sortierte Kopie zurückgibt, ohne das Original zu berühren. Das ist nicht nur kürzer, sondern drückt die Absicht klarer aus.
with(index, value) ist die nicht-mutierende Alternative zu array[index] = value: Es gibt eine neue Kopie des Arrays zurück, bei der das Element am angegebenen Index durch den neuen Wert ersetzt wurde. Für immutable-state-Patterns in React – wo man setState(items.map((item, i) => i === idx ? newItem : item)) schreibt – ist with eine elegante Abkürzung: setState(items.with(idx, newItem)). Diese vier neuen Array-Methoden sind in allen modernen Browsern und Node.js 20+ verfügbar.
// ES2023 non-mutating array methods
const scores = [42, 17, 89, 55, 31];
// toSorted — returns new sorted array, original unchanged
const sorted = scores.toSorted((a, b) => b - a); // [89, 55, 42, 31, 17]
console.log(scores); // [42, 17, 89, 55, 31] — untouched
// toReversed — returns new reversed array
const reversed = scores.toReversed(); // [31, 55, 89, 17, 42]
// toSpliced — returns copy with elements replaced/removed
const withoutFirst = scores.toSpliced(0, 1); // [17, 89, 55, 31]
const withInsert = scores.toSpliced(2, 0, 100); // [42, 17, 100, 89, 55, 31]
// with — returns copy with one element replaced
const corrected = scores.with(1, 99); // [42, 99, 89, 55, 31]
// flatMap: map + flat(1) in one pass
const sentences = ["Hello World", "JavaScript Arrays", "Modern Methods"];
const words = sentences.flatMap(s => s.split(" "));
// ["Hello", "World", "JavaScript", "Arrays", "Modern", "Methods"]
// flatMap as combined filter+map (return [] to exclude)
const items = [1, -2, 3, -4, 5];
const positiveDoubled = items.flatMap(n => n > 0 ? [n * 2] : []);
// [2, 6, 10]
// findLast / findLastIndex (ES2023)
const events = [
{ type: "login", user: "alice", ts: 1000 },
{ type: "action", user: "alice", ts: 2000 },
{ type: "login", user: "bob", ts: 3000 },
];
const lastLogin = events.findLast(e => e.type === "login");
// { type: "login", user: "bob", ts: 3000 }
7. Methoden verketten: Lesbarkeit vs. Performance
Das Verketten von Array-Methoden (method chaining) ist ein Markenzeichen funktionaler Programmierung in JavaScript. data.filter(...).map(...).reduce(...) liest sich fast wie eine natürlichsprachliche Beschreibung des Datenflusses. Aber: Jede Methode in der Kette erzeugt ein neues intermediäres Array. Bei kleinen Arrays (unter ~1000 Elemente) ist das irrelevant. Bei großen Arrays oder häufigen Aufrufen in Animationsloops kann das Intermediate-Array-Problem die Performance spürbar beeinflussen.
Für Performance-kritische Szenarien gibt es zwei Ansätze: Erstens, reduce einsetzen, um mehrere Operationen in einem einzigen Durchlauf zu kombinieren – ein einziges intermediäres Objekt statt mehrerer Arrays. Zweitens, Lazy-Evaluation-Bibliotheken wie Transducer oder Generator-basierte Pipelines verwenden, die Elemente "on demand" durchreichen, ohne intermediate Arrays zu erzeugen. Für die meisten Anwendungen ist optimiertes Verketten von Array-Methoden aber vorzeitige Optimierung – Lesbarkeit und Wartbarkeit haben höhere Priorität als der theoretische Performance-Gewinn durch gesparte Intermediate-Arrays.
8. Object.groupBy und Map.groupBy (ES2024)
Object.groupBy ist eine statische Methode, die ein Iterable nach einem Schlüssel gruppiert und ein null-prototypisches Objekt zurückgibt, bei dem jeder Schlüssel einem Array der passenden Elemente zugeordnet ist. Das ist der native Ersatz für das reduce-Gruppierungs-Pattern, das jahrelang der Standard-Idiom für diese Aufgabe war. Map.groupBy macht dasselbe, gibt aber eine Map zurück – mit dem Vorteil, dass beliebige Werte (nicht nur Strings) als Schlüssel dienen können, was bei Objekten als Schlüssel unverzichtbar ist.
Beide Methoden akzeptieren als erstes Argument ein beliebiges Iterable – nicht nur Arrays. Das macht sie zu leistungsfähigen Array-Methoden für Sets, Maps und Generator-Ausgaben. Die Callback-Funktion gibt den Schlüssel zurück, unter dem das Element eingruppiert werden soll. Für dynamische Schlüssel, die nicht als Objekteigenschaft taugen (z.B. Objekte, Symbole, Zahlen außerhalb gültiger Property-Namen), ist Map.groupBy die richtige Wahl. Browser-Support: Chrome 117+, Firefox 119+, Safari 17.4+, Node.js 21+.
9. Array-Methoden im Vergleich
Die Wahl der richtigen Array-Methode hängt von drei Faktoren ab: dem gewünschten Ausgabe-Typ (Array, Einzelwert, Boolean, Objekt), dem Mutationsverhalten und dem Performance-Profil. Die folgende Tabelle gibt einen schnellen Überblick.
| Methode | Ausgabe | Mutiert Original? | Bricht früh ab? |
|---|---|---|---|
| map | Neues Array (gleiche Länge) | Nein | Nein |
| filter | Neues Array (≤ Länge) | Nein | Nein |
| find / findLast | Einzelnes Element oder undefined | Nein | Ja – beim ersten Treffer |
| reduce | Beliebiger Typ | Nein | Nein |
| toSorted / toReversed | Neues Array | Nein (ES2023) | Nein |
Mironsoft
JavaScript-Entwicklung, Code-Reviews und Performance-Optimierung
Codebase mit veralteten Array-Patterns modernisieren?
Wir migrieren bestehende JavaScript-Codebases auf moderne Array-Methoden, führen Code-Reviews durch und schulen Teams im funktionalen Programmierstil.
Code-Review
Analyse bestehender Array-Patterns auf Mutation, Lesbarkeit und Performance
Refactoring
Migration auf toSorted, toReversed, flatMap und Object.groupBy
Schulung
Team-Workshops zu funktionalen Array-Methoden und ES2023+ Features
10. Zusammenfassung
Moderne Array-Methoden in JavaScript ermöglichen es, Daten deklarativ und ohne Mutation zu transformieren. map transformiert, filter selektiert, reduce akkumuliert, flatMap flacht und transformiert in einem Schritt. Die ES2023-Ergänzungen toSorted, toReversed, toSpliced und with schließen die Lücke zwischen mutierenden Klassikern und dem Bedarf nach immutablem Verhalten in modernen Frontends. findLast und findLastIndex ergänzen die Suchmethoden um die Rückwärtsrichtung, ohne das Original-Array umkehren zu müssen.
Die Wahl der richtigen Array-Methode ist keine Stilfrage: Sie drückt die Absicht des Codes aus, verhindert unbeabsichtigte Mutationen und macht Tests einfacher. Für Teams, die auf React, Vue oder andere reaktive Frameworks setzen, ist die konsequente Nutzung nicht-mutierender Array-Methoden eine direkte Investition in weniger Bugs durch State-Mutation-Fehler – eine der häufigsten Fehlerquellen in Frontend-Anwendungen.
Moderne JavaScript Array-Methoden — Das Wichtigste auf einen Blick
Transformation & Selektion
map für 1:1-Transformation, filter für Selektion, find/findLast für Einzelelement-Suche. find bricht beim ersten Treffer ab – effizienter als filter bei großen Arrays.
reduce & flatMap
reduce für komplexe Akkumulation (Summierung, Gruppierung). flatMap für map + flat(1) in einem Durchlauf. Für Lesbarkeit explizite filter+map-Ketten bevorzugen.
ES2023-Neuheiten
toSorted, toReversed, toSpliced, with – nicht-mutierende Alternativen zu sort(), reverse(), splice() und direkter Index-Zuweisung. Perfekt für React-State.
ES2024: groupBy
Object.groupBy und Map.groupBy ersetzen das reduce-Gruppierungs-Pattern. Map.groupBy für nicht-string Schlüssel. Support: Chrome 117+, Firefox 119+, Node.js 21+.