Array.prototype.reduce()
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
O método reduce()
executa uma função reducer (fornecida por você) para cada elemento do array, resultando num único valor de retorno.
Experimente
A função reducer recebe quatro parâmetros:
- Acumulador (
acc
) - Valor Atual (
cur
) - Index Atual (
idx
) - 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( acumulador, valorAtual[, index[, array]] )[, valorInicial])
Parâmetros
callback
-
Função que é executada em cada valor no array (exceto no primeiro, se nenhum
valorInicial
for passado); recebe quatro argumentos: acumulador
-
É o valor inicial (ou o valor do callback anterior). Este valor inicia com o
valorInicial
e será retornado na última iteração. valorAtual
-
Opcional. O índice do elemento atual que está sendo processado no array. Começa a partir do index
0
se umvalorInicial
for fornecido. Do contrário, começa do index1
. valorInicial
-
Opcional. Valor a ser usado como o primeiro argumento da primeira chamada da função
callback
. Se nenhumvalorInicial
é fornecido, o primeiro elemento do array será usado como o valor inicial doacumulador
e ovalorAtual
não será lido. Chamarreduce()
em uma array vazia sem valor inicial retornará um erro.
Valor retornado
O valor que resulta da execução da função de callback do "reducer" até a conclusão de todo o array.
Descrição
O método reduce()
executa a função de callback
uma vez para cada elemento presente no array, excluindo furos (valores indefinidos), recebendo quatro argumentos:
- acumulador - valor inicial (ou o valor do callback anterior),
- valorAtual - o valor do elemento atual
- index - o índice atual e
array
- 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íveis. Se o valorInicial
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 reduce() executará o callback da função começando a partir do índice 1 (index 1), pulando o primeiro índice (index 0
). Se o valorInicial
for passado como argumento, a função irá começar no index 0.
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 o 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 prover um valorInicial
, porque existem até quatro 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 que o seguinte uso de reduce() tenha ocorrido:
[0, 1, 2, 3, 4].reduce(function (acumulador, valorAtual, index, array) {
return acumulador + valorAtual;
});
// 10
O callback será invocado quatro vezes, com os argumentos e valores em cada chamada sendo:
acumulador |
valorAtual | index | 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 usar 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 valorInicial
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 |
index |
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
let total = [0, 1, 2, 3].reduce(function (acumulador, valorAtual) {
return acumulador + valorAtual;
}, 0);
// retorna 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 um array, 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;
}, valorInicial);
console.log(soma); // retorna 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); // retorna 6
Redução de um array de arrays
let reduzido = [
[0, 1],
[2, 3],
[4, 5],
].reduce(function (acumulador, valorAtual) {
return acumulador.concat(valorAtual);
}, []);
// reduzido é [0, 1, 2, 3, 4, 5]
Utilizando uma arrow function:
let reduzido = [
[0, 1],
[2, 3],
[4, 5],
].reduce((acumulador, valorAtual) => acumulador.concat(valorAtual), []);
Contando valores iguais em um objeto
let nomes = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];
let quantidadeNomes = nomes.reduce(function (todosNomes, nome) {
if (nome in todosNomes) {
todosNomes[nome]++;
} else {
todosNomes[nome] = 1;
}
return todosNomes;
}, {});
// quantidade de nomes:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
Agrupando objetos por uma propriedade
let pessoas = [
{ nome: "Alice", idade: 21 },
{ nome: "Max", idade: 20 },
{ nome: "Jane", idade: 20 },
];
function agruparPor(objetoArray, propriedade) {
return objetoArray.reduce(function (acc, obj) {
let key = obj[propriedade];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});
}
let grupodePessoas = agruparPor(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
Nota:
Se você estiver usando um ambiente compatível com Set
and Array.from()
, você pode usar let orderedArray = Array.from(new Set(myArray))
para obter um array em que os itens duplicados tenham sido removidos.
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]
Substituindo .filter().map() por .reduce()
Usar Array.filter()
seguido por Array.map()
faz com que o array seja percorrido duas vezes. Você pode obter o mesmo resultado percorrendo o array apenas uma vez com Array.reduce()
, o que é, portanto, mais eficiente. (Se você gosta de for loops, você pode usar filter e map percorrendo o array apenas uma vez com Array.forEach()
).
const numbers = [-5, 6, 2, 0];
const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => {
if (currentValue > 0) {
const doubled = currentValue * 2;
accumulator.push(doubled);
}
return accumulator;
}, []);
console.log(doubledPositiveNumbers); // [12, 4]
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
});
Escrever map usando reduce
if (!Array.prototype.mapUsingReduce) {
Array.prototype.mapUsingReduce = function (callback, thisArg) {
return this.reduce(function (mappedArray, currentValue, index, array) {
mappedArray[index] = callback.call(thisArg, currentValue, index, array);
return mappedArray;
}, []);
};
}
[1, 2, , 3].mapUsingReduce(
(currentValue, index, array) => currentValue + index + array.length,
); // [5, 7, , 10]
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
Specification |
---|
ECMAScript Language Specification # sec-array.prototype.reduce |
Compatibilidade com navegadores
BCD tables only load in the browser