Array.prototype.reduce() reduziert ein Array zu einem einzigen Wert — eine Summe, ein aggregiertes Objekt, ein neues Array. Der Callback bekommt einen Akkumulator und das aktuelle Element und liefert den neuen Akkumulator-Wert. Mit Initial-Wert ist das Verhalten klar und sicher; ohne Initial-Wert wird das erste Element als Start genommen — bei leerem Array gibt es einen TypeError. reduce ist eine der mächtigsten, aber auch eine der häufigst überstrapazierten Methoden — für viele Anwendungen ist eine spezifischere Methode lesbarer.
Signatur & Grundform
arr.reduce(
callback: (acc: U, el: T, i: number, arr: T[]) => U,
initialValue?: U
): Uconst zahlen = [1, 2, 3, 4, 5];
// Summe — der Klassiker
const summe = zahlen.reduce((acc, n) => acc + n, 0);
console.log(summe); // 15
// Produkt
const produkt = zahlen.reduce((acc, n) => acc * n, 1);
console.log(produkt); // 12015
120acc ist der Akkumulator, der von einer Iteration zur nächsten weitergereicht wird. Der Initial-Wert (0, 1, [], {} etc.) bestimmt den Startwert und den Typ des Ergebnisses.
Initial-Wert — fast immer setzen
Ohne Initial-Wert nimmt reduce das erste Element als Start. Das kann sinnvoll sein (z.B. bei Summe), führt aber bei leerem Array zu TypeError.
const arr = [1, 2, 3];
// Mit Initial: sicher
console.log(arr.reduce((a, b) => a + b, 0)); // 6
// Ohne Initial: erstes Element wird Start
console.log(arr.reduce((a, b) => a + b)); // 6
// ABER: leeres Array ohne Initial → TypeError
try {
[].reduce((a, b) => a + b);
} catch (e) {
console.log('Fehler:', e.message);
}
// Mit Initial: immer sicher
console.log([].reduce((a, b) => a + b, 0)); // 06
6
Fehler: Reduce of empty array with no initial value
0Faustregel: immer einen Initial-Wert übergeben. Macht den Code expliziter und sicher gegen leere Inputs.
Group-By — Klassisches Beispiel
Vor ES2024 war reduce der Standard-Weg, ein Array zu gruppieren. Heute gibt es Object.groupBy direkt.
const items = [
{ kategorie: 'frucht', name: 'Apfel' },
{ kategorie: 'gemuese', name: 'Karotte' },
{ kategorie: 'frucht', name: 'Birne' },
{ kategorie: 'gemuese', name: 'Salat' },
];
// Pre-ES2024: mit reduce
const gruppen = items.reduce((acc, item) => {
const key = item.kategorie;
if (!acc[key]) acc[key] = [];
acc[key].push(item.name);
return acc;
}, {});
console.log(gruppen);
// ES2024: Object.groupBy — direkt
const gruppen2 = Object.groupBy(items, item => item.kategorie);
console.log(Object.keys(gruppen2));{ frucht: [ 'Apfel', 'Birne' ], gemuese: [ 'Karotte', 'Salat' ] }
[ 'frucht', 'gemuese' ]In neuem Code: Object.groupBy nutzen. reduce-Version bleibt in Legacy-Code reichlich.
Statistik in einem Pass
Multiple Aggregationen lassen sich in einem reduce kombinieren — effizienter als mehrere separate map/filter-Durchläufe.
const punkte = [85, 92, 78, 95, 88];
const stats = punkte.reduce(
(acc, p) => ({
summe: acc.summe + p,
min: Math.min(acc.min, p),
max: Math.max(acc.max, p),
anzahl: acc.anzahl + 1,
}),
{ summe: 0, min: Infinity, max: -Infinity, anzahl: 0 }
);
const durchschnitt = stats.summe / stats.anzahl;
console.log({ ...stats, durchschnitt });{
summe: 438,
min: 78,
max: 95,
anzahl: 5,
durchschnitt: 87.6
}reduce als kombinierter map + filter
Wenn man arr.filter(p).map(f) in einer einzigen Iteration ausführen will, ist reduce das Werkzeug.
const items = [1, 2, 3, 4, 5, 6, 7, 8];
// Klassisch: zwei Iterationen
const a = items.filter(n => n % 2 === 0).map(n => n * 10);
console.log(a); // [20, 40, 60, 80]
// Mit reduce: eine Iteration
const b = items.reduce((acc, n) => {
if (n % 2 === 0) acc.push(n * 10);
return acc;
}, []);
console.log(b); // [20, 40, 60, 80]
// flatMap für eine Iteration mit „leer-zurückgeben-um-zu-filtern":
const c = items.flatMap(n => n % 2 === 0 ? [n * 10] : []);
console.log(c);[ 20, 40, 60, 80 ]
[ 20, 40, 60, 80 ]
[ 20, 40, 60, 80 ]Die flatMap-Variante ist oft die lesbarste Form für „filter + map" in einem Pass.
reduceRight — von hinten nach vorn
Das spiegelbildliche Pendant: iteriert von rechts nach links.
const arr = ['a', 'b', 'c', 'd'];
// Konkatenieren von links
console.log(arr.reduce((acc, x) => acc + x, '')); // 'abcd'
// Konkatenieren von rechts
console.log(arr.reduceRight((acc, x) => acc + x, '')); // 'dcba'abcd
dcbaIn Praxis selten — meist reicht reduce mit angepasster Logik.
Wann besser nicht reduce?
reduce ist mächtig, aber für viele Use-Cases zu generisch. Spezifischere Methoden sind oft lesbarer:
| Aufgabe | Statt reduce besser |
|---|---|
| Werte aufsummieren | reduce ist hier idiomatisch |
| Min/Max finden | Math.max(...arr), Math.min |
| Gruppierung | Object.groupBy (ES2024) |
| Array in Map | new Map(arr) direkt |
| Array invertieren | arr.toReversed() / arr.reverse() |
| Suchen erstes Element | find |
| Erfüllen alle/eines | every / some |
| Flach klopfen | arr.flat() |
Wer reduce für etwas verwendet, das mit einer dieser spezifischen Methoden klarer wäre — schreibt absichtlich „smart code" auf Kosten der Lesbarkeit.
Async Reduce — sequenziell mit Promise-Chain
reduce ist synchron, eignet sich aber für sequenzielle async-Ketten:
async function process(x) {
await new Promise(r => setTimeout(r, 5));
return x * 2;
}
(async () => {
const arr = [1, 2, 3];
// Sequenziell: reduce mit Promise-Akkumulator
const result = await arr.reduce(async (accP, x) => {
const acc = await accP;
acc.push(await process(x));
return acc;
}, Promise.resolve([]));
console.log(result); // [2, 4, 6]
})();[ 2, 4, 6 ]Verständlicher ist meist eine for-of-Schleife mit await — die Promise-Reduce-Pattern braucht es nur, wenn man unbedingt funktional bleiben will.
Besonderheiten
Initial-Wert immer setzen — Sicherheit vor leerem Array
Ohne Initial-Wert wirft reduce bei leerem Array TypeError. Mit Initial-Wert (auch wenn er 0 oder [] ist) ist die Methode immer sicher.
Akkumulator-Typ vs. Element-Typ können verschieden sein
Sehr typisch: arr.reduce((acc, x) => ({ ...acc, [x]: true }), {}) sammelt aus einem Array ein Object. Der TypeScript-Generic reduce<U> reflektiert das.
Spread-Spread in reduce: O(n²) Performance-Falle
arr.reduce((acc, x) => [...acc, transform(x)], []) ist quadratisch — bei jedem Schritt wird das gesamte acc kopiert. Bei großen Arrays: acc.push(...) statt Spread.
reduce ist mächtig — oft überzogen für simple Aggregationen
Für Min/Max: Math.max(...arr). Für Gruppierung: Object.groupBy. Für Konkatenation: arr.join(''). reduce ist die letzte Wahl, wenn keine spezifische Methode passt.
Object.groupBy seit ES2024 ersetzt reduce-Gruppierung
Object.groupBy(arr, x => x.kategorie) ist deklarativer als ein 5-Zeilen-Reduce. Baseline 2024 — Polyfill für ältere Targets verfügbar.
reduceRight in Praxis selten — meist äquivalent zu reduce
Bei kommutativen Operationen (Summe, Max) ergibt reduceRight dasselbe Resultat. Nur bei nicht-kommutativen (Konkatenation, Compose) ist die Reihenfolge relevant.
Pipe und Compose lassen sich mit reduce ausdrücken
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x); ist eine elegante Eine-Zeilen-Definition für Function-Composition. Das ist eine der seltenen Stellen, an denen reduce wirklich glänzt.
Iterator Helpers (ES2025) — lazy reduce kommt
Iterator.prototype.reduce erlaubt Reduce auf einem unendlichen oder lazy Iterator. Praktisch für Streaming-Pipelines, bei denen das gesamte Array nicht erst materialisiert werden soll.
Weiterführende Ressourcen
Externe Quellen
- Array.prototype.reduce() – MDN
- Array.prototype.reduceRight() – MDN
- Object.groupBy() – MDN
- Array.prototype.reduce – ECMAScript Spec