Array.prototype.reduce()

Il metodo reduce() applica una funzione  su un array di valori ( da sinistra verso destra )  che accumula iterativamente, attraverso una determinata operazione,  ciascun elemento restituendo in output un unico valore(ridotto).

var sum = [0, 1, 2, 3].reduce(function(a, b) {
  return a + b;
}, 0);
// la somma è 6

var list1 = [[0, 1], [2, 3], [4, 5]];
var list2 = [0, [1, [2, [3, [4, [5]]]]]];

const flatten = arr => arr.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
flatten(list1); // returns [0, 1, 2, 3, 4, 5]
flatten(list2); // returns [0, 1, 2, 3, 4, 5]

Sintassi

arr.reduce(callback, [initialValue])

Parametri

callback
Funzione da eseguire su ciascun valore dell'array, prendendo 4 argomenti:
accumulator
Il valore accumulato precedentemente ritornato nella ultima invocazione del callback, o il valore iniziale, se fornito.(Vedi sotto).
currentValue
Il valore corrente dell'array , processato.
currentIndex
L' indice dell'elemento corrente dell'array, processato. Inizia con indice 0, se viene fornito un  initialValue i, con indice 1 altrimenti.
array
L'array sul quale effettuare reduce.
initialValue
Opzionale. Il valore da usare come primo argomento della prima chiamata del callback.

Valore Restituito

Il valore risultante dalla riduzione.

Descrizione

reduce esegue una funzione callback (ricorsiva) una volta per ogni elemento presente nell'array, escludendo i buchi nell'array. Riceve in pasto quattro argomenti:

  • accumulator
  • currentValue
  • currentIndex
  • array

La prima vollta che viene incovato il valore ricorsivo (callback) , accumulator e currentValue può essre uno dei due valori. Se viene fornito initialValue nella chiamata da ridurre , allora accumulator sarà uguale all' initialValue e currentValue sarà uguale al primo elemento dell'array. Se non viene fornito alcun initialValue , allora accumulator sarà uguale al primo valore nell'array e currentValue sarà uguale al secondo.

Nota: Se initialValue non viene fornito, reduce eseguirà la funzione ricorsiva iniziando dall'indive 1, saltando il valore 0. Se initialValue viene fornito, inizierà con l'indice a 0.

Se l'array è vuoto e non viene fornito alcun initialValue , verrà lanciata l'eccezione TypeError. Se l'array ha solamente un elemento (indipendentemente dalla posizione) e non è fornito alcun initialValue , o initialValue è fornito ma l'array è vuoto , il solo valore verrà restituito senza invocare la ricorsione (callback).

E' raccomandabile fornire sempre un valore iniziale perchè ci sono tre possibili output senza initialValue,come mostrato nel seguente esempio.

var maxCallback = ( pre, cur ) => Math.max( pre.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );

// reduce() senza valore iniziale
[ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
[ { x: 22 }            ].reduce( maxCallback ); // { x: 22 }
[                      ].reduce( maxCallback ); // TypeError

// map/reduce; la soluzione migliore che funziona anche per array vuoti
[ { x: 22 }, { x: 42 } ].map( el => el.x )
                        .reduce( maxCallback2, -Infinity );

Come funziona reduce

Supponiamo si incorra nel seguente uso di reduce:

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
});

La ricorsione (callback) verrà chiamata quattro volte, con argomenti e valori di ritorno a ciascuna chiamata come segue:

callback accumulator currentValue currentIndex array return value

prima 

chiamata

0 1 1 [0, 1, 2, 3, 4] 1

seconda 

chiamata

1 2 2 [0, 1, 2, 3, 4] 3

terza

chiamata

3 3 3 [0, 1, 2, 3, 4] 6

quarta

chiamata

6 4 4 [0, 1, 2, 3, 4] 10

Il valore restituito da  reduce sarà quello dell'ultima chiamata della callback  (10).

Puoi anche fornire una Arrow Function al posto di una funzione completa. Il codice seguente fornisce lo stesso risultato del codice visto sopra:

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

Se avessi fornito un valore iniziale come secondo argomento a cui applicare reduce, il risultato sarebbe stato il seguente:

[0,1,2,3,4].reduce( (accumulator, currentValue, currentIndex, array) => {
  return accumulator + currentValue;
}, 10);
callback accumulator currentValue currentIndex array return value

prima

chiamta

10 0 0 [0, 1, 2, 3, 4] 10

seconda

chiamta

10 1 1 [0, 1, 2, 3, 4] 11

terza

chiamata

11 2 2 [0, 1, 2, 3, 4] 13

quarta

chiamata

13 3 3 [0, 1, 2, 3, 4] 16

quinta 

chiamata

16 4 4 [0, 1, 2, 3, 4] 20

L'ultima chiamata ritorna il valore (20) che vienre restituito come risultato della funzione reduce.

Esempi

Somma di tutti i valori di un array

var sum = [0, 1, 2, 3].reduce(function(a, b) {
  return a + b;
}, 0);
// sum is 6

Alternativametne scritta con le arrow function:

var total = [ 0, 1, 2, 3 ].reduce( ( acc, cur ) => acc + cur, 0 );

Appiattimento di array di array

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

Altrimenti, scritta con arrow function:

var flattened = [[0, 1], [2, 3], [4, 5]].reduce( ( acc, cur ) => acc.concat(cur), [] );


Conteggio delle istanze di un valore in un oggetto

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 }

Limitare arrays in array di oggetti attraverso spread operator e initial vlaues

// friends - un array di oggetti 
// dove il campo "books" dell'oggetto - lista dei libri preferiti 
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 - lista che conterrà tutti i libri degli amici +  
// lista addizionale contentua in initialValue
var allbooks = friends.reduce(function(prev, curr) {
  return [...prev, ...curr.books];
}, ["Alphabet"]);

// allbooks = ["Alphabet", "Bible", "Harry Potter", "War and peace", 
// "Romeo and Juliet", "The Lord of the Rings", "The Shining"]

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;
    }
  });
}

Se hai bisogno di usare motori JavaScript obsoleti che non supportano Object.defineProperty, è meglio non usare il metodo polyfill Array.prototype , perchè non puoi rendere l'array non-numerabile

Specificazioni

Specification Status Comment
ECMAScript 5.1 (ECMA-262)
The definition of 'Array.prototype.reduce' in that specification.
Standard Definizione iniziale. Implementata in JavaScript 1.8.
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Array.prototype.reduce' in that specification.
Standard  
ECMAScript Latest Draft (ECMA-262)
The definition of 'Array.prototype.reduce' in that specification.
Draft  

Compatibilità browser

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support (Yes) 3.0 (1.9) 9 10.5 4.0
Feature Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support (Yes) (Yes) (Yes) (Yes) (Yes) (Yes)

Vedi anche

Tag del documento e collaboratori

 Hanno collaborato alla realizzazione di questa pagina: mnemosdev, Cesarec88
 Ultima modifica di: mnemosdev,