GroupBy e Sum e lista

Tenho uma lista List<Recebimento>, onde minha classe recebimento tem vários campos: nome, cpf, data de pagamento, valor pago entre outros dados.

Minha duvida é, eu consigo dar um groupby (cpf) e um sum (valorPago) em uma lista assim como faria em SQL.

Pergunto pois tenho nesta lista vários pagamentos, e eu preciso agrupas eles para ter o valor pago por cada cpf.

Sem%20t%C3%ADtulo

1 curtida

Qual é a sua SQL normal, poste ela, dela vamos te ajudar a fazer o Agrupamento e Soma!

Então, essa lista vem de um arquivo Excel, não tenho select em banco, senão ficaria muito fácil.

Estou lendo sobre collections mas não sei se estou no caminho certo.

Bom, fiz algo assim, mas não sei se seria a melhor forma, eu ordeno a lista e vou somando, e criando uma nova lista para adicionar os valores agrupados.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package lista;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 *
 * @author 
 */
public class Processar {

    public Processar() {
        List<DadoslstDados = new ArrayList<>();
        List<DadosnovaDados = new ArrayList<>();

        lstDados.add(new Dados("Fabiano", "111", 25.00));
        lstDados.add(new Dados("zé", "555", 18.68));
        lstDados.add(new Dados("Juliano", "222", 15.00));
        lstDados.add(new Dados("Fabiano", "111", 11.00));
        lstDados.add(new Dados("Pedro", "333", 150.50));
        lstDados.add(new Dados("Pedro", "333", 151.50));
        lstDados.add(new Dados("Mario", "444", 10));
        lstDados.add(new Dados("Fabiano", "111", 50.00));
        lstDados.add(new Dados("Pedro", "333", 200.00));

        // lstDados sem ordenação 
        for (Dados a : lstDados) {
            System.out.println(a.getNome() + " " + a.getCpf() + " " + a.getValor());
        }

        Collections.sort(lstDados, Comparator.comparing(Dados::getNome));

        System.out.println("*********************************************");
        // lstDados ordenada por nome
        for (Dados a : lstDados) {
            System.out.println(a.getNome() + " " + a.getCpf() + " " + a.getValor());
        }

        String nome = "";
        Dados dadoProcessando = new Dados();

        for (int i = 0; i < lstDados.size(); i++) {
            Dados registroAtual = lstDados.get(i);

            if (nome.isEmpty()) {
                nome = registroAtual.getNome();

                dadoProcessando = registroAtual;
                continue;
            }

            if (nome.equalsIgnoreCase(registroAtual.getNome())) {
                dadoProcessando.setValor(dadoProcessando.getValor() + registroAtual.getValor());
            } else {
                nome = registroAtual.getNome();
                novaDados.add(dadoProcessando);

                dadoProcessando = registroAtual;
            }

            if (lstDados.size() <= i + 1) {
                novaDados.add(dadoProcessando);
            }
        }

        System.out.println("*********************************************");
        // Nova lista com os dados agrupados
        for (Dados a : novaDados) {
            System.out.println(a.getNome() + " - " + a.getCpf() + " - " + a.getValor());
        }
    }
}

Se alguém tiver uma sugestão melhor, sera bem vinda. :heartbeat:

é fácil quando se coloca todas as informações na pergunta original, nenhum momento foi dito Excel e isso compromete as respostas, preste atenção na hora de criar uma pergunta e se atente a dizer tudo relacionado.

Pode ser o caminho, mas, não temos o código original, cade o seu código?

Você pega os dados como?
O Agrupamento pode ser feito com um for ou while (uma estrutura de repetição, somando os que já existem) …

Em java não há nada tão simples quanto em sql para fazer isso.

Seu problema tem duas partes:

  • fazer a soma por cpf

  • pegar uma versão de nome e outro dado qualquer no resultado final (Em sql, você precisaria adicionar esses campos também no group by, ou usar um max/min neles).

Para a primeira parte, você pode usar a stream api:

Map<String, Long> somaDeValorPagoPorCpf = recebimentos.stream()
        .collect(Collectors.groupingBy(Recebimento::getCpf,
            Collectors.summingLong(Recebimento::getValorPago)));

Pode ser meio chato entender, mas o nome dos métodos é bem descriptivo: você agrupa pelo valor do método getCpf ao mesmo tempo usa a soma do método getValorPago para gerar um total por cpf.

Na segunda parte, idealmente, você deveria gerar um objeto para representar os outros dados que não geram totais (nome, outro dado qualquer). Para facilitar, vamos pegar um dos objetos Recebimento como base, e usar nome e outro dado qualquer de lá:

Map<String, Recebimento> recebimentoBasePorCpf = recebimentos.stream()
        .collect(Collectors.toMap(Recebimento::getCpf, Function.identity(), (r1, r2) -> r1));

Ou seja, para cada cpf, escolhemos um Recebimento de onde usar as informaçoes de Nome e Outro Dado Qualquer.

Agora é percorrer os dois maps que tem para gerar a saída.

1 curtida