Array.prototype.reduce()

Esta tradução está em andamento.

O método reduce()executa uma função reducer (provida por você) para cada membro do array, resultando num único valor de retorno.

A função reducer é alimentada por quatro parâmetros:

  1. Acumulador (acc)
  2. Valor Atual (cur)
  3. Index Atual (idx)
  4. Array original (src)

O valor de retorno da sua função reducer é atribuída ao acumulador. O acumulador, com seu valor atualizado, é repassado para cada iteração subsequente pelo array, que por fim, se tornará o valor resultante, único, final.

Sintaxe

array.reduce(callback[, valorInicial])

Parâmetros

callback
Função que é executada em cada valor no array, recebe quatro argumentos:

Acumulador
O valor retornado na última invocação do callback, ou o argumento Valor Inicial, se fornecido. (exemplo abaixo).
valorAtual
O elemento atual que está sendo processado no array.
indice
O índice do elemento atual que está sendo processado no array.
array
O array ao qual a função reduce() foi chamada.
valorInicial
Opcional. Objeto a ser usado como o primeiro argumento da primeira chamada da função callback. Chamar reduce() em uma array vazia sem valor inicial é um erro.

Valor de retorno

O valor que resulta da redução.

Descrição

O reduce()  executa a função de callback uma vez para cada elemento presente no array, excluindo furos (valores indefinidos) , recebendo quatro argumentos:

  • o valor inicial (ou o valor do callback anterior),
  • o valor do elemento corrente,
  • o índice corrente e
  • o array onde a iteração está ocorrendo.

A primeira vez que o callback é chamado, o Acumulador e o valorAtual podem ter um de dois valores possíveisSe o Valor Inicial tiver sido fornecido na chamada à função reduce(), então o Acumulador será igual ao valorInicial e o valorAtual será igual ao primeiro valor no array. Caso nenhum valorInicial seja fornecido, Acumulador será igual ao primeiro valor no array, e valorAtual será igual ao segundo.

Nota: Se o valorInicial não tiver sido passado como argumento, então Acumulador será igual ao primeiro valor no array e valorAtual será igual ao segundo.

Se a array estiver vazia e o valorInicial não tiver sido informado, uma exceção do tipo TypeError será lançada. Se a array possuir somente um elemento (independente da posição) e valorInicial não tiver sido fornecido, ou se valorInicial for fornecido, mas a array estiver vazia, o valor será retornado sem que a função de callback seja chamada.

É  mais seguro provir um valor inicial, porque existem tres possíveis saídas sem o valorInicial como mostrado no exemplo:

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

// reduce() sem valores iniciais
[ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
[ { x: 22 }            ].reduce( maxCallback ); // { x: 22 }
[                      ].reduce( maxCallback ); // TypeError

// map/reduce; melhor solução, funciona para vetores vazios e tambem para vetores grandes 
[ { x: 22 }, { x: 42 } ].map( el => el.x )
                        .reduce( maxCallback2, -Infinity );

Como funciona o reduce()

Suponha o seguinte uso de reduce tenha ocorrido:

[0, 1, 2, 3, 4].reduce(function(Acumulador, valorAtual, indice, array) {
  return Acumulador + valorAtual;
});
// 10

O callback será invocado quatro vezes com os argumentos e retorna os valores em cada chamada, como sendo:

Acumulador valorAtual indice array valor de retorno
primeira chamada 0 1 1 [0, 1, 2, 3, 4] 1
segunda chamada 1 2 2 [0, 1, 2, 3, 4] 3
terceira chamada 3 3 3 [0, 1, 2, 3, 4] 6
quarta chamada 6 4 4 [0, 1, 2, 3, 4] 10

O valor retornado pelo reduce será o da última chamada à callback (10).

Você também pode dar uma Arrow Function em vez de uma função completa. O código abaixo produz a mesma saída que o código do bloco acima:

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

Se você informar um valor inicial como o segundo argumento de reduce, o resultado será:

[0, 1, 2, 3, 4].reduce(function(acumulador, valorAtual, indice, array) {
  return acumulador + valorAtual;
}, 10);

// 20
callback Acumulador valorAtual indice array valor de retorno
primeira chamada 10 0 0 [0, 1, 2, 3, 4] 10
segunda chamada 10 1 1 [0, 1, 2, 3, 4] 11
terceira chamada 11 2 2 [0, 1, 2, 3, 4] 13
quarta chamada 13 3 3 [0, 1, 2, 3, 4] 16
quinta chamada 16 4 4 [0, 1, 2, 3, 4] 20

O retorno da última chamada 20,é retornado como resultado da função reduce().

Exemplos

Soma todos os valores de uma array

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

outra alternativa é usar uma arrow function:

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

Soma de valores de um objeto de um array

Para resumir os valores contidos em uma matriz de objetos, você deve fornecer um valorInicial, para que cada item passe por sua função.

var valorInicial = 0;
var soma = [{x: 1}, {x: 2}, {x: 3}].reduce(function (acumulador, valorAtual) {
    return acumulador + valorAtual.x;
},valorAtual)

console.log(soma) // loga 6

Utilizando uma arrow function:

var valorInicial = 0;
var soma = [{x: 1}, {x: 2}, {x: 3}].reduce(
    (acumulador , valorAtual) => acumulador + valorAtual.x
    ,valorInicial 
);

console.log(soma) // loga 6

Redução de um array de arrays

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

Utilizando uma arrow function:

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

Contando valores iguais em um objeto

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 }

Agrupando objetos por uma propriedade

var pessoas = [
  { nome: 'Alice', idade: 21 },
  { nome: 'Max', idade: 20 },
  { nome: 'Jane', idade: 20 }
];

function agruparPor(objetoArray, propriedade) {
  return objetoArray.reduce(function (acc, obj) {
    var key = obj[propriedade];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}

var grupodePessoas = groupBy(pessoas, 'idade');
// grupodePessoas é:
// { 
//   20: [
//     { nome: 'Max', idade: 20 }, 
//     { nome: 'Jane', idade: 20 }
//   ], 
//   21: [{ nome: 'Alice', idade: 21 }] 
// }

Juntando arrays contidos num array de objetos usando o operador spread e o valorInicial

// friends - um array de objetos 
// onde o campo "books" é a lista de livros favoritos 
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 que contém todos os livros de friends +
// lista adicional contida em valorInicial
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'
// ]

Removendo itens duplicados num array

let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
let result = arr.sort().reduce((init, current) => {
    if (init.length === 0 || init[init.length - 1] !== current) {
        init.push(current);
    }
    return init;
}, []);
console.log(result); //[1,2,3,4,5]

Rodando promises em sequência

/**
 * Roda promises de um promise array de uma maneira encadeada
 *
 * @param {array} arr - promise arr
 * @return {Object} promise object
 */
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
  });

Polyfill

Array.prototype.reduce foi adicionado ao padrão ECMA-262  na quinta edição; e portanto, pode não estar presente em todas as implementações do padrão. Você pode contornar isso inserindo o código a seguir no início de seus scripts, permitindo o uso do reduce() em implementações que não possuem suporte nativo a ele.

// Etapas de produção para o ECMA-262, Edition 5, 15.4.4.21
// Referencia: http://es5.github.io/#x15.4.4.21
if (!Array.prototype.reduce) {
  Array.prototype.reduce = function(callback /*, valorInicial*/) {
    'use strict';
    if (this == null) {
      throw new TypeError('Array.prototype.reduce chamado é nulo (null) ou indefinido (undefined)');
    }
    if (typeof callback !== 'function') {
      throw new TypeError(callback + ' não é uma função')
    }
    var t = Object(this), len = t.length >>> 0, k = 0, value;
    if (arguments.length == 2) {
      value = arguments[1];
    } else {
      while (k < len && !(k in t)) {
        k++; 
      }
      if (k >= len) {
        throw new TypeError('Reduce possui um array vazio sem um valor inicial');
      }
      value = t[k++];
    }
    for (; k < len; k++) {
      if (k in t) {
        value = callback(value, t[k], k, t);
      }
    }
    return value;
  };
}

Especificações

Especificação Status Comentário
ECMAScript 5.1 (ECMA-262)
The definition of 'Array.prototype.reduce' in that specification.
Padrão

Definição inicial. Implemetada no JavaScript 1.8.

ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Array.prototype.reduce' in that specification.
Padrão

Compatibilidade do Browser

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
reduceChrome Full support 3Edge Full support 12Firefox Full support 3IE Full support 9Opera Full support 10.5Safari Full support 4.1WebView Android Full support ≤37Chrome Android Full support 18Firefox Android Full support 4Opera Android Full support YesSafari iOS Full support 4Samsung Internet Android Full support 1.0nodejs Full support Yes

Legend

Full support  
Full support

Leia também