La méthode reduce() applique une fonction qui est un « accumulateur » et qui traite chaque valeur d'une liste (de la gauche vers la droite) afin de la réduire à une seule valeur.

Syntaxe

arr.reduce(callback)
arr.reduce(callback, valeurInitiale)

Paramètres

callback
La fonction à exécuter sur chaque valeur de la liste, elle prend quatre arguments en entrée :
accumulateur
La valeur précédemment retournée par le dernier appel du callback, ou valeurInitiale, si elle est fournie (voir ci-après) (c'est la valeur « accumulée » au fur et à mesure des appels
valeurCourante
La valeur de l'élément courant actuellement manipulé dans le tableau.
index
L'index de l'élément courant actuellement manipulé dans le tableau.
array
Le tableau sur lequel on a appelé la méthode reduce().
valeurInitiale Facultatif
Une valeur utilisée comme premier argument lors du premier appel de la fonction callback. Si on appelle reduce() sur un table vide sans fournir de valeur initiale, on aura une erreur.

Valeur de retour

La valeur obtenue grâce à la fonction de réduction.

Description

reduce() exécute la fonction callback une fois pour chaque élément présent dans le tableau et ignore les éléments vides du tableau. La fonction callback utilise quatre arguments :

  1. L'accumulateur (la valeur retournée par le précédent appel de la fonction callback), ou la valeur initiale s'il sagit du premier appel ;
  2. la valeur de l'élément courant ;
  3. l'index de l'élément courant ;
  4. le tableau parcouru par la méthode.

La première fois que la fonction callback est appelée, valeurInitiale et valeurCourante peuvent correspondre à un ou deux éléments. Si valeurInitiale est fournie dans l'appel de reduce(), alors accumulateur sera égale à valeurInitiale et valeurCourante sera égale à la première valeur de la liste. Si valeurInitiale n'est pas fournie, alors accumulateur sera égale à la première valeur de la liste, et valeurCourante sera alors égale à la seconde.

Autrement dit, si valeurInitiale n'est pas fournie, reduce exécutera la fonction de rappel à partir de l'indice 1 et la première valeur du tableau (d'indice 0) sera utilisée pour valeurInitiale.

En considérant le code suivant :

[0, 1, 2, 3, 4].reduce(function(accumulateur, valeurCourante, index, array){
  return accumulateur + valeurCourante;
});

La fonction callback sera appelée quatre fois, avec les arguments et les valeurs de retour de chaque appel suivant :

  accumulateur valeurCourante index array valeur retournée
premier appel 0 1 1 [0,1,2,3,4] 1
deuxième appel 1 2 2 [0,1,2,3,4] 3
troisième appel 3 3 3 [0,1,2,3,4] 6
quatrième appel 6 4 4 [0,1,2,3,4] 10

La valeur retournée par reduce() sera alors celle du dernier appel de la callback (ici 10).

Il est aussi possible d'utiliser une fonction fléchée au lieu d'une fonction classique. Le code suivant, par exemple, produit le même résultat que l'exemple précédent :

[0, 1, 2, 3, 4].reduce( 
  (accumulateur, valeurCourante) => accumulateur + valeurCourante;
);

Si on fournit une valeur initiale comme second argument à l'appel de reduce(), le résultat sera alors le suivant :

[0, 1, 2, 3, 4].reduce(function(accumulateur, valeurCourante, index, array){
  return accumulateur + valeurCourante;
}, 10);
  accumulateur valeurCourante index array valeur retournée
premier appel 10 0 0 [0,1,2,3,4] 10
deuxième appel 10 1 1 [0,1,2,3,4] 11
troisième appel 11 2 2 [0,1,2,3,4] 13
quatrième appel 13 3 3 [0,1,2,3,4] 16
cinquième appel 16 4 4 [0,1,2,3,4] 20

Ici, la valeur renvoyée par reduce() serait 20.

Exemples

Additionner toutes les valeurs d'un tableau

var total = [0, 1, 2, 3].reduce((a, b)=> a + b,0);
// total == 6

Additionner les valeurs d'une propriétés pour un tableau d'objets

Pour additionner les valeurs d'une propriété donnée des objets d'un tableau, il sera nécessaire de fournir une valeur initiale afin que tous les éléments soient parcourus :

var valeurInitiale = 0;
var somme = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulateur, valeurCourante) {
    return accumulateur + valeurCourante.x;
}, valeurInitiale);

console.log(somme); // affiche 6 dans la console

On peut également écrire une version plus concise avec les fonctions fléchées :

var valeurInitiale = 0;
var somme = [{x: 1}, {x:2}, {x:3}].reduce(
    (accumulateur, valeurCourante) => accumulateur + valeurCourante.x
    , valeurInitiale
);

console.log(somme); // affiche 6 dans la console

Aplatir une liste de listes

var applati = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
    return a.concat(b);
});
// aplati vaut [0, 1, 2, 3, 4, 5]

Utiliser le paramètre valeurInitiale

var amis = [
  { "nom": "Quentin", "livres": ["City Hall", "Harry Potter"]},
  { "nom": "Alice", "livres": ["L'Avare", "Les Fleurs du Mal"]}
]

var tousLivres = amis.reduce(function(prev, curr) {
  return [...prev, ...curr.livres];
}, ["Perceval"]);

// tousLivres = ["Perceval", "City Hall", "Harry Potter", 
//               "L'Avare", "Les Fleurs du Mal"]

Exécuter une suite de promesses stockées dans un tableau

/**
 * Exécuter un enchaînement de promesses à partir d'un tableau
 *
 * @param {array} arr - un tableau de promesses
 * @return {Object} un objet Promise
 */
function runPromiseInSequense(arr) {
  return arr.reduce((promiseChain, currentPromise) => {
    return promiseChain.then((chainedResult) => {
      return currentPromise(chainedResult)
        .then((res) => res)
    })
  }, Promise.resolve());
}

// promise function 1
function p1() {
  return new Promise((resolve, reject) => {
    resolve(5);
  });
}

// promise function 2
function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2);
  });
}

// promise function 3
function p3(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 3);
  });
}

const promiseArr = [p1, p2, p3];
runPromiseInSequense(promiseArr)
  .then((res) => {
    console.log(res);   // 30
  });

Regrouper des objets selon un propriété

var personnes = [
  { nom: "Alice", age: 21 },
  { nom: "Bob", age: 20 },
  { nom: "Charlie", age: 20 }
];

function groupBy(tableauObjets, propriete){
  return tableauObjets.reduce(function (acc, obj) {
    var cle = obj[propriete];
    if(!acc[cle]){
      acc[cle] = [];
    }
    acc[cle].push(obj);
    return acc;
  }, {});
}

var personnesParAge = groupBy(personnes, "age");
// personnesParAge aura la valeur :
// {
//    20: [
//       { nom: "Bob", age: 20 },
//       { nom: "Charlie", age: 20 }
//    ],
//    21: [{ nom: "Alice", age: 21 }]
// }

Composition de fonctions

// Les briques de base que nous allons composer
const double = x => x + x;
const triple = x => 3 * x;
const quadruple = x => 4 * x;

// Une fonction qui permet d'appliquer une composition
const pipe = (...functions) => input => functions.reduce(
    (acc, fn) => fn(acc),
    input
);

// On crée des fonctions pour multiplier par un facteur donné
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);

// Utilisation
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240

Prothèse d'émulation (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;
    }
  });
}

S'il est vraiment nécessaire de prendre en charge les moteurs JavaScript qui ne prennent pas en charge Object.defineProperty(), mieux vaut ne pas ajouter de prothèses pour les méthodes de Array.prototype car on ne peut pas les rendre non-énumerables.

Spécifications

Spécification État Commentaires
ECMAScript 5.1 (ECMA-262)
La définition de 'Array.prototype.reduce()' dans cette spécification.
Standard Définition initiale.
Implémenté dans JavaScript 1.8
ECMAScript 2015 (6th Edition, ECMA-262)
La définition de 'Array.prototype.reduce()' dans cette spécification.
Standard  
ECMAScript Latest Draft (ECMA-262)
La définition de 'Array.prototype.reduce()' dans cette spécification.
Projet  

Compatibilité des navigateurs

FonctionnalitéChromeEdgeFirefoxInternet ExplorerOperaSafari
Support simple Oui Oui3910.54
FonctionnalitéAndroid webviewChrome for AndroidEdge mobileFirefox for AndroidOpera AndroidiOS SafariSamsung Internet
Support simple Oui Oui Oui4 Oui Oui Oui

Voir aussi

Étiquettes et contributeurs liés au document

Dernière mise à jour par : SphinxKnight,