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 von callback oder initialValue werden verwendet.
currentValue
Das aktuell zu verarbeitende Element des Arrays.
currentIndexOptional
Der Index des aktuellen Elements des Arrays. Beginnt mit dem Index 0, falls initialValue angegeben wurde, ansonsten mit Index 1.
arrayOptional
Das Array, auf dem reduce abgerufen wird.
initialValueOptional
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 von reduce() 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 auf, Ausnahme sind nicht gesetzte Werte. callback hat vier Parameter:

  • accumulator
  • currentValue
  • currentIndex
  • array

Beim ersten Aufruf von callback können accumulator und currentValue einer der beiden Werte sein. Wenn initialValue beim Aufruf von reduce() angegeben wird, ist accumulator gleich dem initialValue und currentValue ist gleich dem ersten Wert im Array. Wenn kein initialValue angegeben wird, ist accumulator gleich mit dem ersten Wert im Array und currentValue 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 vier mal 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() 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 Latest Draft (ECMA-262)
Die Definition von 'Array.prototype.reduce()' in dieser Spezifikation.
Entwurf  

Browserkompatibilität

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid WebviewChrome für AndroidEdge MobileFirefox für AndroidOpera für AndroidiOS SafariSamsung InternetNode.js
Grundlegende UnterstützungChrome Vollständige Unterstützung JaEdge Vollständige Unterstützung JaFirefox Vollständige Unterstützung 3IE Vollständige Unterstützung 9Opera Vollständige Unterstützung 10.5Safari Vollständige Unterstützung 4WebView Android Vollständige Unterstützung JaChrome Android Vollständige Unterstützung JaEdge Mobile Vollständige Unterstützung JaFirefox Android Vollständige Unterstützung 4Opera Android Vollständige Unterstützung JaSafari iOS Vollständige Unterstützung JaSamsung Internet Android Vollständige Unterstützung Janodejs Vollständige Unterstützung Ja

Legende

Vollständige Unterstützung  
Vollständige Unterstützung

Siehe auch

Schlagwörter des Dokuments und Mitwirkende

Zuletzt aktualisiert von: NiklasMerz,