Filipe Colaquecez

Filipe Colaquecez

Reduce - Um poderoso método de array

javascript, array

Nesse artigo irei abordar um pouco sobre o array.reduce e como podemos utilizar ele para resolver diversos desafios!

O reduce é um dos métodos mais incríveis para se manipular arrays e um amigo até brinca que se você não consegue resolver um problema utilizando reduce então o problema é complexo 🤣

Sintaxe

array.reduce(callback( acumulador, valorAtual[, index[, array]] )[, valorInicial]))

O reduce possui 2 parâmetros: uma função de callback que recebe 4 parâmetros: (acumulador, valor atual, índice e o array) sendo o índice e o array, valores opcionais, e o segundo parâmetro do reduce é o valor inicial que também é um parâmetro opcional.

Como ele funciona?

O reduce passa por todos os elementos do seu array, e a cada etapa que retorna, ele atribui o valor do retorno para o seu acumulador e no final ele vai gerar um único valor de qualquer tipo.

Um exemplo bastante utilizado para exemplificar um do seus usos, é somar todos os valores do array, exemplo:

const array = [1, 3, 4, 5, 7]

array.reduce((accumulator, currentValue) => accumulator + currentValue) //20

Para ficar mais claro o seu funcionamento, irei mostrar o mesmo exemplo, porém utilizando o forEach

const array = [1,2,3,5,7]
let total = 0

array.forEach((item => total += item)

console.log(total) // 18

Uma dica é sempre informar o valor inicial como segundo parâmetro do reduce, porque dessa forma evitamos erros caso a nossa lista não esteja iniciada, que seria o mesmo que fazer array || [] 🤓

const array = []

const total = array.reduce((ac, c) => ac + c)

console.log(total) // TypeError: Reduce of empty array with no initial value"

const total = array.reduce((ac, c) => ac + c, 0)

console.log(total) // 0

Algo legal de se saber também, é que se não informarmos o valor inicial para nosso reduce, nosso índice irá começar no 1, ou seja, o "valor inicial" automaticamente irá ser o primeiro elemento do nosso array, caso seja passado um valor inicial, ele será atribuído ao nosso acumulador.

const array = [6, 6, 4]

const total = array.reduce((accumulator, current, index) => {
  console.log("index", index)

  console.log("accumulator", accumulator)

  console.log("current", current)

  return accumulator + current
})

console.log(total) //16

Como output do código anterior, você deverá receber no console os seguintes dados:

index 1

accumulator 6

current 6

index 2

accumulator 12

current 4

Casos de uso

Sempre que tenho tempo livre, gosto de treinar e me desafiar em algoritmos através do Codewars. Recentemente me deparei com um desafio que continha o seguinte enunciado:

The main idea is to count all the occuring characters(UTF-8) in string. If you have string like this aba then the result should be { 'a': 2, 'b': 1 }

Em resumo, se um parâmetro contendo a palavra Filipe fosse passado para essa função, o retorno esperado seria:

{'f': 1, 'i':2, 'l':1, 'p':1, 'e':1}

Para resolver esse desafio, eu utilizei nada mais nada menos que nosso querido reduce.

const count = string => {
  const stringToArray = string.split("")

  const result = stringToArray.reduce(
    (accumulator, current) => ({
      ...accumulator,
      [current]: (accumulator[current] || 0) + 1,
    }),
    {}
  )

  return result
}

console.log(count("filipe"))
// { e: 1, f: 1, i: 2, l: 1, p: 1 }

Como o exercício espera de retorno um objeto, eu iniciei o meu reduce com um objeto vazio {} garantindo sempre um retorno, mesmo que vazio. Pode parecer confuso por que utilizei ...accumulator, vou explicar:

Para cada etapa que o meu reduce percorre o array, eu pego todo o valor do meu accumulator e recrio um novo objeto com os valores anteriores, se eu não fizesse isso ele apenas pegaria o último item ficando:

{
  e: 1
}

Dessa forma conseguimos com o spread, criar nosso objeto com o accumulator e também usando o poder da imutabilidade(em outro post irei abordar desse tema 😁) para deixar nosso código bastante performático, sem side effects e sem precisar criar variáveis mutáveis, incrível né?!

E complementando para quem utiliza redux, pode ver uma certa familiaridade com o reducer, que basicamente funciona da mesma forma, o action.payloaddo redux seria o nosso currentValue e o state seria nosso accumulator.

function birds(state = defaultBirds, action) {
  switch (action.type) {
    case ADD_BIRD:
      return [
        ...state,
        {
          name: action.bird,
          views: 1,
        },
      ]
    default:
      return state
  }
}

E para finalizar, um exemplo que precisei fazer esses dias no trabalho, eu precisava pegar todo o retorno de uma API e criar um objeto de categorias utilizando um campo específico, vou exemplificar para ficar mais claro (utilizando Pokemon como exemplo 😎)

const pokemons = [
  { id: 1, type: "water", name: "Magikarp" },
  { id: 2, type: "eletric", name: "Pikachu" },
  { id: 3, type: "water", name: "Squirtle" },
  { id: 4, type: "fire", name: "Charizard" },
  { id: 5, type: "fire", name: "Ponyta" },
]

Vamos lá precisamos desse retorno:

{
water: [...],
fire: [...],
eletric: [...]
}

E para resolver eu fiz basicamente esse código:

const typePokemon = pokemons.reduce(
  (typesPokemon, pokemon) => ({
    ...typesPokemon,
    [pokemon.type]: typesPokemon[pokemon.type]
      ? [...typesPokemon[pokemon.type], pokemon]
      : [pokemon],
  }),
  {}
)

Eu retorno um objeto e verifico se no meu accumulator(typesPokemon) existe o tipo que estou especificando, caso exista eu passo por spread todo o conteúdo antigo + o novo conteúdo, caso eu verifique que não possuo a categoria no meu objeto eu apenas retorno o novo tipo com a minha posição atual.

Conclusão

É isso, espero que esse post tenha te ajudado de alguma forma, eu dei bastante exemplo do seu uso exatamente para mostrar que ele faz muito mais coisas do que apenas somar os valores de um array, reduce é extremamente poderoso e como pode ver existe diversas situações que o reduce resolve de forma simples e performática, se você tem alguma sugestão ou algo para agregar sinta-se livre para comentar e até conversar comigo no Linkedin! valeu! 🚀

Me acompanhe nas redes

Instagram:

@colaquecez.dev

Compartilhe ;)