Array.prototype.reduce()
Try it
Die reduce()
-Methode reduziert ein Array auf einen einzigen Wert, indem es jeweils zwei Elemente (von links nach rechts) durch eine gegebene Funktion reduziert.
Syntax
arr.reduce(callback[, initialValue])
Parameter
callback
- Funktion, welche auf jeden Wert im Array angewandet wird und vier Argumente hat:
accumulator
- Der kumulierte Wert ist der Rückgabewert von
callback
; der in der zuvor zurückgegebene Wert des letzten Aufrufes voncallback
oderinitialValue
werden verwendet. currentValue
- Das aktuell zu verarbeitende Element des Arrays.
currentIndex
Optional- Der Index des aktuellen Elements des Arrays. Beginnt mit dem Index 0, falls
initialValue
angegeben wurde, ansonsten mit Index 1. array
Optional- Das Array, auf dem
reduce
abgerufen wird.
initialValue
Optional- Der Wert des ersten Arguments, der bei dem ersten Aufruf in
callback
zu benutzt wird. Wenn kein Initialwert angegeben wird, wird das erste Element des Arrays benutzt. Das Aufrufen vonreduce()
auf einem leeren Array ohne Initialwerts führt zu einem Fehler.
Rückgabewert
Der Ergebniswert der Reduzierung.
Beschreibung
reduce()
führt die callback
-Funktion für jedes existierende Element in dem Array aus, Ausnahme sind nicht gesetzte Werte. callback
hat vier Parameter:
accumulator
currentValue
currentIndex
array
Beim ersten Aufruf von callback
sind die Werte von accumulator
und currentValue
in Abhängigkeit vom Parameter initialValue
wie folgt:
- Wenn
initialValue
beim Aufruf vonreduce()
angegeben wird, istaccumulator
gleich deminitialValue
undcurrentValue
ist gleich dem ersten Wert im Array. - Wenn kein
initialValue
angegeben wird, istaccumulator
gleich mit dem ersten Wert im Array undcurrentValue
wird gleich dem zweiten Wert im Array sein.
Hinweis: Wenn initialValue
nicht angegeben wird, wird reduce()
die callback
-Funktion startend beim Index 1 aufrufen, der erste Index wird übersprungen. Wenn initialValue
angegeben ist, wird bei Index 0 begonnen.
Wenn das Array leer ist und kein initialValue
angegeben ist, wird ein TypeError
erzeugt. Wenn das Array nur ein Element hat (die Position ist egal) und kein initialValue
angegeben ist oder wenn initialValue
angegeben ist und das Array leer ist, wird der einzelne Wert sofort zurückgegeben, ohne callback
aufzurufen.
Es ist immer sicherer initialValue
anzugeben, weil es drei mögliche Ergebnisse ohne initialValue
gibt, wie es im folgenden Beispiel gezeigt ist.
var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );
// reduce() without initialValue
[ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
[ { x: 22 } ].reduce( maxCallback ); // { x: 22 }
[ ].reduce( maxCallback ); // TypeError
// map/reduce; better solution, also works for empty or larger arrays
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
Wie reduce() funktioniert
Angenommen die folgende reduce()
Funktion wird genutzt:
[0, 1, 2, 3, 4].reduce(function (accumulator, currentValue, currentIndex, array) {
return accumulator + currentValue;
});
Die callback
-Funktion wird viermal aufgerufen, mit den Parametern und Rückgabewert für jeden Aufruf wir folgend beschrieben:
callback |
accumulator |
currentValue |
currentIndex |
array |
Rückgabewert |
---|---|---|---|---|---|
1. Aufruf | 0 |
1 |
1 |
[0, 1, 2, 3, 4] |
1 |
2. Aufruf | 1 |
2 |
2 |
[0, 1, 2, 3, 4] |
3 |
3. Aufruf | 3 |
3 |
3 |
[0, 1, 2, 3, 4] |
6 |
4. Aufruf | 6 |
4 |
4 |
[0, 1, 2, 3, 4] |
10 |
Der zurückgegebene Wert von reduce()
ist der Rückgabewert der letzten callback
-Funktion (10
).
Es ist zusätzlich möglich eine Arrow-Funktion statt einer ganzen Funktion zu benutzen. Der folgende Quelltext erzeugt die gleiche Ausgabe wie der Vorherige:
[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr );
Wenn als zweiter Parameter von reduce()
initialValue
angegeben ist, wird das Ergebnis wie folgt aussehen:
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => {
return accumulator + currentValue;
}, 10);
callback |
accumulator |
currentValue |
currentIndex |
array |
Rückgabewert |
---|---|---|---|---|---|
1. Aufruf | 10 |
0 |
0 |
[0, 1, 2, 3, 4] |
10 |
2. Aufruf | 10 |
1 |
1 |
[0, 1, 2, 3, 4] |
11 |
3. Aufruf | 11 |
2 |
2 |
[0, 1, 2, 3, 4] |
13 |
4. Aufruf | 13 |
3 |
3 |
[0, 1, 2, 3, 4] |
16 |
5. Aufruf | 16 |
4 |
4 |
[0, 1, 2, 3, 4] |
20 |
Der von reduce()
zurückgegebene Wert ist in diesem Fall 20
.
Beispiele
Alle Elemente eines Arrays summieren
var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
// sum is 6
Alternativ als Arrow-Funktion geschrieben:
var total = [ 0, 1, 2, 3 ].reduce(
( accumulator, currentValue ) => accumulator + currentValue,
0
);
Summe von Werten in einem Objektarray
Um in einem Array von Objekten Werte aufzusummieren, muss ein Initialwert angegeben werden, so dass jedes Element in der Funktion durchlaufen wird.
var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
return accumulator + currentValue.x;
},initialValue)
console.log(sum) // logs 6
Alternativ als Arrow-Funktion geschrieben:
var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(
(accumulator, currentValue) => accumulator + currentValue.x
,initialValue
);
console.log(sum) // logs 6
Geschachtelte Arrays zu einem flachen Array machen
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(accumulator, currentValue) {
return accumulator.concat(currentValue);
},
[]
);
// flattened is [0, 1, 2, 3, 4, 5]
Alternativ als Arrow-Funktion geschrieben:
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
( accumulator, currentValue ) => accumulator.concat(currentValue),
[]
);
Zählen von Instanzen eines Wertes in einem Objekt
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
Objekte nach einer Eigenschaft gruppieren
var people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
function groupBy(objectArray, property) {
return objectArray.reduce(function (acc, obj) {
var key = obj[property];
if(!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});
}
var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age:21 }]
// }
Verbinden von Arrays in einem Array von Objekten mithilfe des Spread-Operators und von initialValue
// friends - an array of objects
// where object field "books" - list of favorite books
var friends = [{
name: 'Anna',
books: ['Bible', 'Harry Potter'],
age: 21
}, {
name: 'Bob',
books: ['War and peace', 'Romeo and Juliet'],
age: 26
}, {
name: 'Alice',
books: ['The Lord of the Rings', 'The Shining'],
age: 18
}];
// allbooks - list which will contain all friends' books +
// additional list contained in initialValue
var allbooks = friends.reduce(function(accumulator, currentValue) {
return [...accumulator, ...currentValue.books];
}, ['Alphabet']);
// allbooks = [
// 'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
// 'Romeo and Juliet', 'The Lord of the Rings',
// 'The Shining'
// ]
Duplikate in einem Array entfernen
let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
let result = arr.sort().reduce((accumulator, current) => {
const length = accumulator.length;
if (length === 0 || accumulator[length - 1] !== current) {
accumulator.push(current);
}
return accumulator;
}, []);
console.log(result); //[1,2,3,4,5]
Sequenzielle Abarbeitung von Promises
/**
* Runs promises from array of functions that can return promises
* in chained manner
*
* @param {array} arr - promise arr
* @return {Object} promise object
*/
function runPromiseInSequence(arr, input) {
return arr.reduce(
(promiseChain, currentFunction) => promiseChain.then(currentFunction),
Promise.resolve(input)
);
}
// promise function 1
function p1(a) {
return new Promise((resolve, reject) => {
resolve(a * 5);
});
}
// promise function 2
function p2(a) {
return new Promise((resolve, reject) => {
resolve(a * 2);
});
}
// function 3 - will wrapped in a resolved promise by .then()
function f3(a) {
return a * 3;
}
// promise function 4
function p4(a) {
return new Promise((resolve, reject) => {
resolve(a * 4);
});
}
const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10)
.then(console.log); // 1200
Funktionskomposition ermöglicht Pipelining
// Building-blocks to use for composition
const double = x => x + x;
const triple = x => 3 * x;
const quadruple = x => 4 * x;
// Function composition enabling pipe functionality
const pipe = (...functions) => input => functions.reduce(
(acc, fn) => fn(acc),
input
);
// Composed functions for multiplication of specific values
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);
// Usage
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240
Mapfunktion mit reduce
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
return this.reduce(function(mappedArray, currentValue, index, array) {
mappedArray[index] = callback.call(thisArg, currentValue, index, array);
return mappedArray;
}, []);
};
}
[1, 2, , 3].map(
(currentValue, index, array) => currentValue + index + array.length
); // [5, 7, , 10]
Polyfill
// Production steps of ECMA-262, Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, 'reduce', {
value: function(callback /*, initialValue*/) {
if (this === null) {
throw new TypeError( 'Array.prototype.reduce ' +
'called on null or undefined' );
}
if (typeof callback !== 'function') {
throw new TypeError( callback +
' is not a function');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// Steps 3, 4, 5, 6, 7
var k = 0;
var value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}
// 3. If len is 0 and initialValue is not present,
// throw a TypeError exception.
if (k >= len) {
throw new TypeError( 'Reduce of empty array ' +
'with no initial value' );
}
value = o[k++];
}
// 8. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kPresent be ? HasProperty(O, Pk).
// c. If kPresent is true, then
// i. Let kValue be ? Get(O, Pk).
// ii. Let accumulator be ? Call(
// callbackfn, undefined,
// « accumulator, kValue, k, O »).
if (k in o) {
value = callback(value, o[k], k, o);
}
// d. Increase k by 1.
k++;
}
// 9. Return accumulator.
return value;
}
});
}
Wenn es wirklich notwendig ist veraltete JavaScript-Umgebungen zu benutzen, die Object.defineProperty() (en-US)
nicht unterstützen, ist es besser Array.prototype
nicht komplett zu ersetzen, weil man es nicht non-enumerable machen kann.
Spezifikationen
Spezification | Status | Kommentar |
---|---|---|
ECMAScript 5.1 (ECMA-262) Die Definition von 'Array.prototype.reduce()' in dieser Spezifikation. |
Standard | Initiale Definition. Implementiert in JavaScript 1.8. |
ECMAScript 2015 (6th Edition, ECMA-262) Die Definition von 'Array.prototype.reduce()' in dieser Spezifikation. |
Standard | |
ECMAScript (ECMA-262) Die Definition von 'Array.prototype.reduce()' in dieser Spezifikation. |
Lebender Standard |
Browserkompatibilität
BCD tables only load in the browser