Array.prototype.reduce()
Reduce vai reduzir TODOS os seus problemas a apenas um: como usar o reduce?
- Sant’Anna, Israel (2021)
Tudo bem, TODOS é claramente um exagero, mas o método reduce é capaz de resolver MUITOS problemas. Inclusive, a funcionalidade de outros métodos, como map
, filter
, find
, some
, every
e vários outros (todos talvez?) pode ser implementada utilizando o reduce!
A princípio a sintaxe e o funcionamento do reduce pode parecer complicada e difícil, mas é mais fácil do que parece. Vamos por partes!
SINTAXE
O método reduce pode receber dois argumentos:
[].reduce(reducer[, valor inicial]);
Posição | Argumento | Obrigatório/Opcional | Observações |
---|---|---|---|
1 | função reducer | Obrigatório | |
2 | valor inicial | Opcional | Se fornecido que será passado para a função reducer |
REDUCER
A função reducer recebe de dois a quatro argumentos, sendo:
const reducer = (acc, curr[, index[, originalArray]]) => { ... };
Posição | Argumento | Obrigatório/Opcional | Observações |
---|---|---|---|
1 | acumulador (valorAnterior) (retorno) | Obrigatório | o valor resultante da chamada anterior da função reducer |
2 | valorAtual | Obrigatório | o valor do item na chamada atual |
3 | indexAtual | Opcional | o index do item na chamada atual |
4 | arrayOriginal | Opcional | o array onde o método reduce foi chamado |
Para cada item do array, a função reducer é executada. Por exemplo:

A saída no console será:
acc: 1
curr: 2
acc: 3
curr: 3
6
Ué?! Mas se a função é executada para cada item do array, porque o valor do primeiro item não é logado (curr: 1
)?
Isso acontece porque quando não utilizamos o segundo argumento do método reduce (o valor inicial), a função reducer atribui ao acumulador o valor do primeiro item, e já segue operando do segundo item em diante. Para evitar isso, devemos passar um valor inicial, como:

E a saída no console será:
acc: 0
curr: 1
acc: 1
curr: 2
acc: 3
curr: 3
6
E por que não é logado o último valor do acumulador (acc: 6
)? Isso acontece porque quando o método reduce já avaliou o último item, a função reducer não é executada novamente, e o valor do acumulador é retornado, sendo atribuído, neste caso à constante sum
.
Esse exemplo é bem simples e não chega perto do poder que o reduce dá a nós desenvolvedores! Vamos mostrar alguns exemplos um pouco mais complexos e na próxima seção explicar cada um passo a passo! :D
EXEMPLOS
Transformar um array em objeto:

Transformar um objeto em array:

Verificar uma condição e retornar apenas true/false:

Remover itens duplicados de um array:

Mesclar dois arrays em um só, na ordem original:

Mesclar dois arrays em um só, um após o outro:
Iterando pelo menor array:

Iterando pelo maior array:

Mesclar dois (ou mais) arrays em um objeto:

PASSO A PASSO
Alguns dos exemplos anteriores podem ser fáceis de entender, mas para que fique bem claro, vamos passo a passo em cada um, extraindo e expandindo a função reducer para entender melhor o que está acontecendo.
Transformar um array em objeto:

1: name
e age
são variáveis obtidas através da desestruturação do elemento atual
Se observarmos bem, cada elemento de peopleArray é um array onde o primeiro elemento (peopleArray[0]) é o nome, e o segundo elemento (peopleArray[1]) é a idade.
Quando desestruturamos, já atribuímos um nome a cada elemento deste array, logo se não tivéssemos desestruturado, mas usado, por exemplo, uma variável chamada curr, poderíamos atribuir as variáveis desta forma:
const name = curr[0]
(que é o mesmo que peopleArray[0])
const age = curr[1]
(que é o mesmo que peopleArray[1])
2: Para criar o objeto utilizamos o index como chave e as propriedades name
e age
.
Como index é variável, precisamos usar a notação de colchetes (bracket notation) para que a chave não seja literalmente ‘index’, mas sim, a cada iteração index é avaliado e a chave é corretamente atribuída.
Se as chaves deste objeto mais interno tivessem outro nome, não poderíamos utilizar a abreviação nos nomes de propriedade (property shorthand).
3: Object.assign() é uma forma de se atribuir propriedades a um objeto.
Funciona basicamente assim:
Ele recebe dois argumentos, nesta ordem: target e source.
As propriedades de source são copiadas para target.
Se a propriedade já existia, será sobrescrita em target.
Se não existia, será adicionada a target.
O retorno deste método é target já modificado com as propriedades novas;
Mais detalhes e exemplos disponíveis na documentação.
4: Por fim, retornamos o acumulador com o novo objeto (personObject) incluído na chave especificada (index).
Lembrando que a função reducer vai ser chamada para cada item do array, então a cada iteração um novo objeto vai ser criado e incluído no acumulador!
Exemplo:
Iteração 1:
acc:{}
name:Pedro
age:20
index:0
personObject:0: { name: 'Pedro', age: 20 }
Iteração 2:
acc:{ 0: { name: 'Pedro', age: 20 } }
name:Tiago
age:25
index:1
personObject:1: { name: 'Tiago', age: 25 }
Iteração 3:
acc:{
0: { name: 'Pedro', age: 20 },
1: { name: 'Tiago', age: 25 }
}
name:João
age:30
index:2
personObject:2: { name: 'João', age: 30 }
Resultado final:
acc:{
0: { name: 'Pedro', age: 20 },
1: { name: 'Tiago', age: 25 },
2: { name: 'João', age: 30 }
}
5: Incluímos um objeto vazio como valor inicial, pois caso contrário o primeiro item do array seria utilizado na primeira iteração e não obteríamos o resultado esperado.
(Experimente e veja o que acontece! :D)
Transformar um objeto em array:

1: A cada iteração, o elemento atual será posto dentro de um novo array e concatenado ao acumulador.
Exemplo:
Iteração 1:
acc:[]
curr:['israelss', 'israelss.github.io']
O que será concatenado:[ ['israelss', 'israelss.github.io'] ]
Iteração 2:
acc:[ ['israelss', 'israelss.github.io'] ]
curr:['trybe', 'betrybe.com']
O que será concatenado:[ ['trybe', 'betrybe.com'] ]
Resultado final:
acc:[ ['israelss', 'israelss.github.io'], ['trybe', 'betrybe.com'] ]
Isso é necessário para que o resultado seja um array de arrays, pois o método concat
“extrai” um nível do array.
Mais detalhes e exemplos disponíveis na documentação.
Verificar uma condição e retornar apenas true/false:

1/5: A cada iteração, será primeiro verificado se o acumulador é true
ou false
. Se for true
será retornado o valor resultante da segunda expressão (um condicional ternário). Se for false
a segunda expressão nem é avaliada e este é o valor retornado nesta iteração. Como, nesta implementação, é impossível mudar de false
para true
, uma vez que o acumulador se tornar false
este será o resultado final retornado pela função reducer.
2 (Função evenReducer): Caso o item atual seja par, o acumulador será true
(3), caso contraŕio, o acumulador será false
(4).
6 (Função oddsReducer): Caso o item atual seja ímpar, o acumulador será true
(7), caso contraŕio, o acumulador será false
(8).
Exemplo:
isAllEven1:
Iteração 1:
acc:true
curr:2
O que será retornado:true
Iteração 2:
acc:true
curr:4
O que será retornado:true
Iteração 3:
acc:true
curr:6
O que será retornado:true
Resultado final:
acc:true
isAllOdds3:
Iteração 1:
acc:true
curr:1
O que será retornado:true
Iteração 2:
acc:true
curr:2
O que será retornado:false
Iteração 3:
acc:true
curr:3
O que será retornado:false
Resultado final:
acc:false
Repare que mesmo 3
sendo ímpar, o condicional ternário não é avaliado, porque o acumulador já é false
.
Remover itens duplicados de um array:

1: A cada iteração, será avaliado se o acumulador contém o elemento atual. Se contiver, o acumulador será retornado sem nenhuma modificação (2). Se não contiver, o elemento atual será concatenado ao acumulador e o array resultante é retornado (3).
Exemplo:
Iteração 1:
acc:[]
curr:1
O que será retornado:[1]
Iteração 2:
acc:[1]
curr:1
O que será retornado:[1]
Iteração 3:
acc:[1]
curr:2
O que será retornado:[1, 2]
Iteração 4:
acc:[1, 2]
curr:3
O que será retornado:[1, 2, 3]
Iteração 5:
acc:[1, 2, 3]
curr:4
O que será retornado:[1, 2, 3, 4]
Iteração 6:
acc:[1, 2, 3, 4]
curr:4
O que será retornado:[1, 2, 3, 4]
Iteração 7:
acc:[1, 2, 3, 4]
curr:5
O que será retornado:[1, 2, 3, 4, 5]
Resultado final:
acc:[1, 2, 3, 4, 5]
Mesclar dois arrays em um só, na ordem original:

1: A cada iteração, será concatenado ao acumulador o elemento atual do array de origem.
2: Em seguida, será concatenado ao acumulador o elemento com o mesmo índice do atual, porém este elemento pertence ao segundo array. Por isso é necessário utilizar o argumento index
, para que seja possível acessar a mesma posição em outro array
3: O acumulador com os dois elementos, em ordem, é retornado.
Exemplo:
Iteração 1:
acc:[]
curr:1
index:0
O que será concatenado (1):1
O que será concatenado (2):4
Iteração 2:
acc:[1, 4]
curr:2
index:1
O que será concatenado (1):2
O que será concatenado (2):5
Iteração 3:
acc:[1, 4, 2, 5]
curr:3
index:2
O que será concatenado (1):3
O que será concatenado (2):6
Resultado final:
acc:[1, 4, 2, 5, 3, 6]
Mesclar dois arrays em um só, um após o outro:
Iterando pelo menor array:

1: Para este exemplo não utilizamos nem o acumulador, nem o elemento atual de cada iteração!
2: A cada iteração, adicionamos ao array
original o elemento do maior array que tem o mesmo index
que o elemento atual.
3: O acumulador neste caso vai ser o array original, modificado a cada iteração.
Exemplo:
Iteração 1:
acc:[]
index:0
array:[1, 2, 3]
O que será concatenado:4
Iteração 2:
acc:[1, 2, 3, 4]
index:1
array:[1, 2, 3, 4]
O que será concatenado:5
Iteração 3:
acc:[1, 2, 3, 4, 5]
index:2
array:[1, 2, 3, 4, 5]
O que será concatenado:6
Resultado final:
array:[1, 2, 3, 4, 5, 6]
Vale lembrar que normalmente, o método reduce
não altera o array original, mas nesta implementação o array original é alterado!
Iterando pelo maior array:

1: Assim como no exemplo anterior, neste não utilizamos nem o acumulador, nem o elemento atual de cada iteração!
2: Diferente do exemplo anterior, nós precisamos incluir essa verificação pois como estamos iterando pelo maior array, vai chegar um momento que não vai existir o elemento do menor array que tem o mesmo index
que o elemento atual. Mas enquanto esse momento não chega, a cada iteração, adicionamos ao array
original o elemento do menor array que tem o mesmo index
que o elemento atual.
3: O acumulador neste caso também vai ser o array original, modificado a cada iteração.
Exemplo:
Iteração 1:
acc:[]
index:0
array:[4, 5, 6, 7]
O que será concatenado:1
Iteração 2:
acc:[4, 5, 6, 7, 1]
index:1
array:[4, 5, 6, 7, 1]
O que será concatenado:2
Iteração 3:
acc:[4, 5, 6, 7, 1, 2]
index:2
array:[4, 5, 6, 7, 1, 2]
O que será concatenado:3
Iteração 4:
acc:[4, 5, 6, 7, 1, 2, 3]
index:3
array:[4, 5, 6, 7, 1, 2, 3]
O que será concatenado: nada (arrayFive[3] === undefined
, então não satisfaz a condição necessária para que haja opush
(2))Resultado final:
array:[4, 5, 6, 7, 1, 2, 3]
Vale lembrar que, assim como no exemplo anterior, o array original é alterado!
Mesclar dois (ou mais) arrays em um objeto:

A primeira parte deste exemplo tem o mesmo efeito do primeiro exemplo (Transformar um array em objeto), então vamos passar rápido por ela e focar mais na segunda parte.
1: O argumento index
será utilizado como chave do objeto a ser adicionado.
2: Como não estamos atuando em cima de um array só, não podemos usar aqui a desestruturação e precisamos definir o valor da chave name
manualmente.
3: O mesmo se aplica à chave age
. E aqui utilizamos novamente o argumento index
, para pegar corretamente o elemento do array que contém as idades.
Exemplo:
peopleDataObject1:
Iteração 1:
acc:{}
index:0
curr:Pedro
agesArray[0]:20
O que será incluído no acumulador:0: { name: 'Pedro', age: 20 }
Iteração 2:
acc:{ 0: { name: 'Pedro', age: 20 } }
index:1
curr:Tiago
agesArray[1]:25
O que será incluído no acumulador:1: { name: 'Tiago', age: 25 }
Iteração 3:
acc:{
0: { name: 'Pedro', age: 20 },
1: { name: 'Tiago', age: 25 }
}
index:2
curr:João
agesArray[2]:30
O que será incluído no acumulador:2: { name: 'João', age: 30 }
Resultado final:
acc:{
0: { name: 'Pedro', age: 20 },
1: { name: 'Tiago', age: 25 },
2: { name: 'João', age: 30 }
}
Na segunda parte usamos o argumento index
para iterar “simultâneamente” pelos três arrays.
E antes de utilizar o Object.assign() para retornar o acumulador com o novo objeto inserido, definimos algumas constantes para podermos utilizar a abreviação nos nomes de propriedade (property shorthand) e simplificar um pouco o código.
1: O argumento curr
será utilizado como a chave name.
2: O elemento do array agesArray que tem o mesmo index
que o elemento atual será utilizado como a chave age.
3: O elemento do array citiesArray que tem o mesmo index
que o elemento atual será utilizado como a chave city.
4: Utilizamos as contantes definidas nos passos anteriores para montar uma string em uma template literal
.
5: Agora utilizamos o Object.assign() para retornar o novo objeto criado usando abreviação nos nomes de propriedade (property shorthand) e inserido no acumulador.
Exemplo:
Para brevidade, o acumulador e o objeto criado não serão explicitados no exemplo abaixo, mas a essa altura você já deve conseguir entender quais serão estes valores a cada iteração.
peopleDataObject2:
Iteração 1:
index:0
curr:Pedro
agesArray[0]:20
citiesArray[0]:Rio de Janeiro
Iteração 2:
index:1
curr:Tiago
agesArray[1]:25
citiesArray[1]:São Paulo
Iteração 3:
index:2
curr:João
agesArray[2]:30
citiesArray[2]:Belo Horizonte
Resultado final:
acc:{
0: {
name: 'Pedro',
age: 20,
city: 'Rio de Janeiro',
greeting: 'Olá, meu nome é Pedro,
tenho 20 anos e
sou de Rio de Janeiro.',
},
1: {
name: 'Tiago',
age: 25,
city: 'São Paulo',
greeting: 'Olá, meu nome é Tiago,
tenho 25 anos e
sou de São Paulo.',
},
2: {
name: 'João',
age: 30,
city: 'Belo Horizonte',
greeting: 'Olá, meu nome é João,
tenho 30 anos e
sou de Belo Horizonte.',
}
}
CONCLUSÃO
Estes são só alguns exemplos do que pode ser feito com o reduce.
Mas é bom salientar que não é porque podemos fazer algo com uma ferramenta que devemos utilizá-la. Existem formas mais adequadas de se realizar algumas tarefas, como por exemplo utilizar flatMap
em vez de reduce
em conjunto com concat
em algumas situações.
Tendo dito isto, fica o convite para utilizar mais o método reduce
nos seus projetos. Leia com carinho e calma a excelente documentação sobre o reduce disponível na MDN, e qualquer dúvida, sugestão ou correção, deixe um comentário abaixo! Até o próximo artigo!