Antonio Guedes
Antonio Guedes21/03/2025 11:25
Share

Como Agregar Dados de Forma Eficiente em JavaScript - Dominando o reduce()

    📌Introdução

    Durante o desenvolvimento de software, muitas vezes precisamos agregar relatĂłrios 📊 que resumem e estruturam dados de forma clara. Isso facilita tanto o entendimento do usuĂĄrio quanto a tomada de decisĂ”es đŸ€”.

    const clientes = [
    {nome: 'Ana', idade: 25, cidade: 'SĂŁo Paulo', sexo: 'F'},
    {nome: 'Fernando', idade: 44, cidade: 'SĂŁo Francisco do Conde', sexo: 'M'},
    {nome: 'JoĂŁo', idade: 19, cidade: 'SĂŁo Chapadinha', sexo: 'M'},
    {nome: 'Bernardo', idade: 31, cidade: 'BrasĂ­lia', sexo: 'M'},
    {nome: 'JĂșlia', idade: 27, cidade: 'Rio de Janeiro', sexo: 'F'},
    ]
    

    Se o usuĂĄrio precisar entender quantos clientes existem em cada cidade ou qual a distribuição por gĂȘnero, ele terĂĄ dificuldades ao analisar os dados dessa forma.

    Felizmente, com o método reduce(), podemos transformar essa estrutura de dados em um formato mais organizado e legível, facilitando sua anålise. 

    📌O que faz o mĂ©todo reduce()?

    O mĂ©todo reduce() Ă© uma função de alta ordem em JavaScript que permite percorrer um array e acumular seus valores de acordo com uma lĂłgica definida pelo programador. Seu principal objetivo Ă© reduzir um conjunto de dados a um Ășnico valor ou estrutura, que pode ser um nĂșmero, string, array ou objeto.

    No exemplo mencionado na introdução, queremos agrupar os clientes por gĂȘnero. Para isso, podemos utilizar reduce() para transformar a lista de clientes em um objeto que contĂ©m a contagem de pessoas do gĂȘnero masculino e feminino, ignorando outras informaçÔes como nome, idade e cidade.

    O resultado esperado seria:

    {clientePorGenero: {masculino: 3, feminino: 2}}
    

    🧑‍🎓Como aprendi o mĂ©todo?

    Ao iniciar no desenvolvimento, percebi que entender lógica de programação e estrutura de dados era fundamental, especialmente para lidar com respostas vindas do banco de dados e retornos de métodos que envolvem diferentes formatos de dados.

    Foi então que decidi usar a IA ChatGPT para me ajudar a melhorar nesse aspecto, começando pelos arrays. Durante a conversa, a IA me desafiou a somar os itens de um array e, instintivamente, tentei usar forEach(). Foi nesse momento que recebi a sugestão de usar reduce(), um método que eu ainda não dominava.

    A partir daí, fui recebendo desafios progressivamente mais complexos, simulando problemas do dia a dia de um desenvolvedor. Com cada exercício, fui compreendendo melhor a utilidade do reduce() e sua aplicação pråtica em problemas reais.

    seuArray.reduce((acumulador, valorAtual, Ă­ndice, arrayOriginal) => {
    
               // LĂłgica de acumulação
    
    }, valorInicial)
    

    O reduce() sempre retorna um valor, que pode ser um nĂșmero, string, objeto ou atĂ© mesmo um array. Esse retorno precisa ser armazenado para ser utilizado posteriormente.

    đŸ€”Quais argumentos o reduce() recebe?

    Acumulador: O valor que é atualizado a cada iteração.

    Valor atual: O item atual do array sendo processado.

    Índice (opcional): A posição do item dentro do array.

    Array original (opcional): O array completo sobre o qual o reduce() estĂĄ sendo executado.

    Valor inicial (opcional): Define o valor inicial do acumulador. Se nĂŁo for informado, o primeiro item do array serĂĄ usado como valor inicial.

    O valor inicial Ă© importante porque determina o formato da saĂ­da. Se for um objeto {}, o resultado serĂĄ um objeto; se for um array [], o retorno serĂĄ um array; se for 0, o resultado serĂĄ um nĂșmero acumulado.

    O reduce() Ă© especialmente Ăștil para resumir e transformar dados, como no caso de um array de objetos, onde ele pode agregar informaçÔes de forma estruturada.

    📌Sintaxe básica

    A sintaxe båsica do reduce envolve sempre a presença de um acumulador e um valor atual, importante para evitar bugs a inicialização do acumulador para evitar retornos inesperados. 

    let max = [0, 1, 2, 3, 4, 5].deduce(function(acc, valorAtual){
    
    if(acc > valorAtual) {acc = acc}
    
    else{acc = valorAtual}
    
    return acc
    }, 0)
    

    📝Explicação:

    •  A função de callback recebe dois parĂąmetros:
    • acc (acumulador): Armazena o maior valor encontrado atĂ© o momento;
    • valorAtual: O nĂșmero atual do array que estĂĄ sendo analisado.
    • A cada iteração, o cĂłdigo verifica se acc Ă© maior que valorAtual:
    • Se for, acc mantĂ©m seu valor.
    • Caso contrĂĄrio, acc assume o valor de valorAtual.
    • Ao final, reduce() retorna o maior valor encontrado, que serĂĄ armazenado na variĂĄvel max.ParĂąmetros (accumulator, currentValue, index, array)

    Neste exemplo, que apenas muda a função de call-back, no exemplo anterior apenas compara os valores entre o acc e o valorAtual, neste exemplo, adiciona ao acc, que estå inicializado com 0, o valorAtual, gerando uma soma a cada iteração.

    Uma aplicação pråtica para esta soma usando reduce seria calcular o total de vendas em um sistema de e-commerce.

    Imagine que vocĂȘ tenha um array apresentando compras realizadas, onde cada item tem um valor:

    const vendas = [100, 250, 75, 300, 150];
    
    const totalVendas = vendas.reduce((acc, valorAtual) => acc + valorAtual, 0)
    
    console.log(`Total de vendas: R$ ${totalVendas}`)
    
    
    
    //saĂ­da: Total de vendas R$ 875
    

    📝Explicação:

    • O acumulador (acc) começa com 0;
    • A cada iteração valorAtual (o valor de uma venda) Ă© somado ao acc;
    • No final, reduce() retorna a soma total das vendas, que pode ser exibida para um administrador da loja.

    Observação importante: Esse mesmo conceito pode ser aplicado para calcular despesas, notas de alunos, pontos de um jogo.

    📌Reduce() na Prática: Agregando dados Complexos

    É possível realizar agrupamentos de dados em um objeto por uma de suas propriedades. Veja o exemplo que segue:

    let pessoas = [
             { nome: “Rosana”, idade: 20},
             { nome: “Max”, idade: 22},
             { nome: “Jane”, idade: 20},
    ]
    function agruparPor(objetoArray, propriedade){
    return objetoArray.reduce((acc, obj) => {
      let key = obj[propriedade];
      if(!acc[key]){acc[key] = [];}
      acc[key].push(obj);
      return acc;
      }, {});
    }
    let grupoDePessoas = agruparPor(pessoas, “idade”)
    
    //SaĂ­da
    //grupoPessoas Ă©:
    //{
    //        20: [
    //                        {nome: “Rosana”, idade: 20},
    //                        {nome: “Jane”, idade: 20},
    //             ], 
    //        22:[{nome: “Max”, idade: 22}]
    //}
    

    đŸ€ŻIntrodução de um problema real 

    Somando Faturamento Mensal 📊

    Imagine que vocĂȘ trabalha para uma empresa que quer analisar seu faturamento mensal. EntĂŁo vocĂȘ deve somar o total de faturamento para cada mĂȘs e apresentar o resumo.

    const transaçÔes = [
             {mes: “Janeiro”, valor: 1000}, 
             {mes: “Fevereiro”, valor: 1500}, 
             {mes: “Janeiro”, valor: 500}, 
             {mes: “Março”, valor: 2000}, 
             {mes: “Fevereiro”, valor: 1000}, 
    ];
    
    function relatorioFaturamento(transacoes){
             return transaçÔes.reduce((acc, transacao) => {
                  const { mes, valor } = transação
                  acc[mes] = acc[mes] ? acc[mes] + valor : valor;
                  return acc;
      }, {});
    }
    console.log(relatoriaFaturamento(transaçÔes))
    // Saída: { Janeiro: 1500, Fevereiro: 2500, Março: 2000 }
    

    Contar FrequĂȘncia de Clientes 👀

    Imagina que vocĂȘ quer saber qual Ă© o cliente mais frequente de teu e-commerce. VocĂȘ tem um array com as compras realizadas:

    const compras = [
             {id: 101, cliente: “Maria Aparecida”, email: “maria.aparecida@email.com”},
             {id: 202, cliente: “Paulo Augusto”, email: “augustos@email.com”},
             {id: 101, cliente: “Maria Aparecida”, email: “maria.aparecida@email.com”},
             {id: 303, cliente: “Raissa Loureto”, email: “louretinho@email.com”},
             {id: 202, cliente: “Paulo Augusto”, email: “augustos@email.com”},
             {id: 101, cliente: “Maria Aparecida”, email: “maria.aparecida@email.com”},
             {id: 404, cliente: “JosĂ© Paulo”, email: “paulo.jose@email.com”},
             {id: 404, cliente: “JosĂ© Paulo”, email: “paulo.jose@email.com”},
             {id: 404, cliente: “JosĂ© Paulo”, email: “paulo.jose@email.com”},
             {id: 202, cliente: “Paulo Augusto”, email: “augustos@email.com”},
             {id: 202, cliente: “Paulo Augusto”, email: “augustos@email.com”},
    ]
    function relatorioVendas(arr) {
     return arr.reduce((acc, { id, cliente }) => {
     acc[cliente] = (acc[cliente] || 0) + 1
     return acc
     }, {})
    }
    
    
    console.log(relatorioVendas(compras))
    // SaĂ­da:
    { 'Maria Aparecida': 3,
     'Paulo Augusto': 4,
     'Raissa Loureto': 1,
     'JosĂ© Paulo': 3 }
    

    Para os casos apresentados, o reduce Ă© muito vantajoso.

    No exemplo do faturamento mensal o uso do reduce Ă© extremante eficiente, e se comparar com o uso de forEach() + if().

    No que se trata da frequĂȘncia de compra pelos clientes o mĂ©todo reduce evita a necessidade de criar objeto auxiliar antes de percorrer os dados.

    Boas PrĂĄticas e Armadilhas Comuns ao Usar reduce()đŸ› ïžsim

    O mĂ©todo reduce() Ă© poderoso, mas pode ser difĂ­cil de entender e manter se nĂŁo for usado corretamente. Aqui estĂŁo algumas boas prĂĄticas e armadilhas que vocĂȘ deve evitar ao utilizĂĄ-lo.

    A ImportĂąncia de Inicializar Corretamente o Acumulador

    A inicialização do acumulador Ă© um dos aspectos mais crĂ­ticos do reduce(). Se ele nĂŁo for inicializado corretamente, vocĂȘ pode obter resultados inesperados ou atĂ© mesmo erros no cĂłdigo.

    ❌Exemplo de erro comum:

    const numeros = [10, 20, 30];
    
    const soma = numeros.reduce((acc, valorAtual) => acc + valorAtual);
    
    console.log(soma); // ERRO se o array estiver vazio
    

    O que estĂĄ acontecendo aqui?

    Caso o reduce() for chamado sem uma inicialização e o array estiver vazio, ele lança um erro porque não hå valor inicial para o acc.

    📌Solução Correta:
    const soma = numeros.reduce((acc, valorAtual) => acc + valorAtual, 0);
    console.log(soma)
    //saĂ­da
    // 60
    

    O 0 garante que, mesmo se o array estiver vazio, o cĂłdigo nĂŁo quebre. Portanto Ă© sempre uma boa prĂĄtica inicializar o acumulador (acc).

    ✅Dicas para Tornar o Código Mais Legível e Evitar Bugs

    reduce() pode gerar cĂłdigo confuso se nĂŁo for bem estruturado. Aqui estĂŁo algumas dicas para tornĂĄ-lo mais legĂ­vel e seguro:

    ✅ Use nomes descritivos para os parñmetros

    Evite nomes genéricos como [a] e [b]. Prefira nomes que indiquem o propósito das variåveis.

    Exemplo ruim:

    const resultado = valores.reduce((a, b) => a + b, 0);
    

    Exemplo melhor:

    const resultado = valores.reduce((acumulador, valorAtual) => acumulador + valorAtual, 0);
    

    ✅ Evite lógica complexa dentro da função de reduce()

    Se sua função reduce() estiver muito complexa, considere dividi-la em funçÔes auxiliares.

    Condigo difĂ­cil de entender:

    const categorias = compras.reduce((acc, { cliente }) => {
    acc[cliente] = acc[cliente] ? acc[cliente] + 1 : 1
    return acc;
    }, {});
    

    Código mais legível com função auxiliar:

    function contarClientes(acc, {cliente}){
     acc[cliente] = (acc[cliente] || 0) + 1
     return acc
    }
    
    const categorias = compras.reduce(contarClientes, {})
    

    Separar a lógica em uma função nomeada melhora a legibilidade e manutenção.

    📌Quando usar reduce() vs map(), filter() e forEach()

    Nem sempre reduce() é a melhor escolha. Veja quando usar cada um dos métodos:

    Método | Quando Usar | Exemplo

    reduce() | Quando vocĂȘ precisa transformar um array em um Ășnico valor (nĂșmero, string, objeto, etc.). | Somar valores, agrupar Ă­tens.

    map() | Quando vocĂȘ deseja transformar cada item do array sem reduzir para um Ășnico valor. | Criar um novo array com os preços com desconto.

    filter() | Quando precisa manter apenas alguns elementos do array | Filtrar produtos acima de R$ 50,00.

    forEach() | QUando deseja apenas iterar sobre o array sem criar um novo. | Exibir mensagens no console.

    Exemplo prĂĄtico comparando reduce(), map(), filter() e forEach()

    const numeros = [1, 2, 3, 4, 5];
    
    // ❌ Exemplo desenecessário de reduce()
    const soma = numeros.reduce((acc, num) => acc + num, 0);
    
    // ✅ Uso correto de map()
    const dobrados = numero.map(num => num * 2); // [2, 4, 6, 8, 10]
    
    //✅ Uso correto de filter()
    const pares = numeros.filter(num => num % 2 === 0); // [2, 4]
    
    // ✅ Uso correto de forEach()
    numero.forEach(num => console.log(num)); // apenas exibe os valores
    

    AĂ­ vocĂȘ deve estar se perguntando - Por que usar o reduce para realizar a soma de itens de um array Ă© um uso desnecessĂĄrio do reduce? E esta Ă© uma excelente pergunta, afinal fizemos vĂĄrios exemplos assim neste artigo. A verdade Ă© que usar o reduce para fazer a soma dos itens de um array Ă© como usar uma bazuca para caçar uma formiga, hĂĄ mĂ©todos mais simples que o reduce para realizar esta operação de somar itens de um array como por exemplo:

    const soma = numeros.sum(); // entretanto funciona em algumas versÔes modernas do JavaScript
    

    Caso nĂŁo haja suporte para array.sum(), outra abordagem Ă© utilizar o for ou o for-of:

    let soma = 0;
    for (let num of numeros){
    soma += num 
    }
    

    Este formato pode ser mais fĂĄcil de entender do que o reduce(), especialmente para iniciantes.

    Quando o reduce() Ă© a melhor escolha?

    Caso estejas lidando com dados mais complexos, onde precisa transformar a estrutura ao mesmo tempo que acumula valores, reduce() se torna extremamente Ăștil.

    Por exemplo, se estivermos somando os valores de objetos dentro de um array:

    const compras = [
     {produto: "Camiseta", preco: 50},
     {produto: "Calça", preco: 100},
     {produto: "TĂȘnis", preco: 200}
    ];
    
    const total = compras.reduce((acc, item) => acc + item.preco, 0);
    
    console.log(total)
    
    //saĂ­da
    // 350
    

    Neste caso, reduce() Ă© a melhor escolha por que estamos transformando um array de objetos em um Ășnico valor (a soma dos preços).

    Portanto, Quando somamaos um array de nĂșmeros simples Ă© ❌ desnecessĂĄrio o uso de reduce(), pois hĂĄ alternativas mais diretas e legĂ­veis. É ✅Ăștil o uso do reduce() quando agregamos dados mais complexos ou precisamos transformar a estrutura ao mesmo tempo.

    O uso do reduce() para somas simples não está errado, necessariamente, mas pode tornar o código menos intuitivo do que outras abordagens 🚀.

    📌Conclusão

    O mĂ©todo reduce() Ă© muito Ăștil para agregar dados, mas pode ser complicado de entender e manter. Assim, sempre inicialize o acumulador corretamente, prefira nomes descritivos para os parĂąmetros, evite lĂłgicas complexas dentro da função reduce() e use map(), filter() ou forEach() quando forem mais adequados. Deste modo seus cĂłdigos serĂŁo mais simples, diretos e legĂ­veis. Usando o reduce teus cĂłdigos terĂŁo um aspecto mais profissional e terĂĄ mais fĂĄcil manutenção. Portanto, use e abuse do mĂ©todo reduce, mas considerando a moderação.

    Share
    Comments (3)
    William Silva
    William Silva - 24/03/2025 07:38

    Muito bom! 👏👏👏

    Antonio Guedes
    Antonio Guedes - 21/03/2025 14:00

    EntĂŁo,

    Eu nunca trabalhei na ĂĄrea, todo este artigo veio de estudar e ver situaçÔes possĂ­veis para utilização. Atualmente trabalho numa ĂĄrea que minhas habilidades nĂŁo sĂŁo utilizadas, mas sempre penso em meu trabalho como utilizaria essas habilidades. Estou trabalhando junto com uma equipe de desenvolvimento de uma aplicação para utilização internacional e imaginei como coletar os dados da aplicação e resumi-las. Tenho um forte desejo de trabalhar com ciĂȘncia de dados, aĂ­ pensei nas situaçÔes.


    Mas na prĂĄtica ainda nĂŁo consegui utilizar meus entendimentos e habilidades. O artigo foi no intuito de um aprendizado ativo.

    DIO Community
    DIO Community - 21/03/2025 12:32

    Antonio, vocĂȘ fez uma excelente explicação sobre como usar o mĂ©todo reduce() de forma eficiente em JavaScript. Seus exemplos prĂĄticos foram muito claros, especialmente ao comparar o uso de reduce() com outros mĂ©todos como map(), filter() e forEach(), ajudando a entender quando Ă© realmente necessĂĄrio usar reduce() e quando outras abordagens sĂŁo mais simples e legĂ­veis.

    Agora, me conta: qual foi a situação mais desafiadora que vocĂȘ encontrou ao usar reduce() em projetos reais? Houve algum momento em que teve que reestruturar a lĂłgica de reduce() para tornar o cĂłdigo mais eficiente ou legĂ­vel?