Necesito ayuda para un caso en Stream con groupingBy Me gustaría poder agrupar por 2 campos diferentes y tener la suma de otros campos BigDecimal, de acuerdo con las diferentes agrupaciones. Aquí está mi entidad:
public class Customer { private String name; private String type; private BigDecimal total; private BigDecimal balance; // Setter, getter }Supongamos que tengo como entrada esta lista:
Customer custa = new Customer("A", "STANDARD", new BigDecimal("1000"), new BigDecimal("1500")); Customer custa1 = new Customer("A", "VIP", new BigDecimal("2000"), new BigDecimal("2500")); Customer custb = new Customer("B", "STANDARD", new BigDecimal("3000"), new BigDecimal("3500")); Customer custc = new Customer("C", "STANDARD", new BigDecimal("4000"), new BigDecimal("4500")); Customer custa2 = new Customer("A", "VIP", new BigDecimal("1500"), new BigDecimal("2500")); List<Customer> listCust = new ArrayList<>(); listCust.add(custa); listCust.add(custa1); listCust.add(custb); listCust.add(custc); listCust.add(custa2);El resultado debe ser
[ {"A", "STANDARD", new BigDecimal("1000"), new BigDecimal("1500")}, {"A", "VIP", new BigDecimal("3500"), new BigDecimal("5000")}, {"B", "STANDARD", new BigDecimal("3000"), new BigDecimal("3500")}, {"C", "STANDARD", new BigDecimal("4000"), new BigDecimal("4500")} ]Tengo un principio de solución, a continuación, pero bloqueo al momento de agregar una segunda agregación para resumir el saldo:
listCust.stream() .collect(Collectors.groupingBy(Customer::getName , Collectors.groupingBy(Customer::getType , Collectors.reducing(BigDecimal.ZERO,Customer::getTotal,BigDecimal::add))) ) .entrySet()Collection<Customer> customers = listCust.stream() .collect(Collectors.toMap( customer -> customer.getName() + '-' + customer.getType(), Function.identity(), (one, two) -> { String name = one.getName(); String type = one.getType(); BigDecimal total = one.getTotal().add(two.getTotal()); BigDecimal balance = one.getBalance().add(two.getBalance()); return new Customer(name, type, total, balance); })).values();Una alternativa al uso de la concatenación de cadenas para el mapa hash intermedio que brinda más flexibilidad podría ser usar una Entry . Esto le permitiría cambiar sus tipos de agrupación y maneja el caso de valores nulos si envuelve la clave o el valor en Optional.ofNullable . Tiene el inconveniente de limitarlo a solo dos elementos para agrupar.
Customer custa = new Customer("A", "STANDARD", new BigDecimal("1000"), new BigDecimal("1500")); Customer custa1 = new Customer("A", "VIP", new BigDecimal("2000"), new BigDecimal("2500")); Customer custb = new Customer("B", "STANDARD", new BigDecimal("3000"), new BigDecimal("3500")); Customer custc = new Customer("C", "STANDARD", new BigDecimal("4000"), new BigDecimal("4500")); Customer custa2 = new Customer("A", "VIP", new BigDecimal("1500"), new BigDecimal("2500")); Customer custa3 = new Customer(null, "VIP", new BigDecimal("1500"), new BigDecimal("2500")); List<Customer> listCust = new ArrayList<>(); listCust.add(custa); listCust.add(custa1); listCust.add(custb); listCust.add(custc); listCust.add(custa2); listCust.add(custa3); Collection<Customer> result = listCust.stream() .collect(Collectors.toMap( c -> Map.entry(Optional.ofNullable(c.getName()), c.getType()), Function.identity(), (c1, c2) -> new Customer(c1.getName(), c1.getType(), c1.getTotal().add(c2.getTotal()), c1.getBalance().add(c2.getBalance())))) .values(); System.out.println(result);outupt (nuevas líneas insertadas para facilitar la lectura)
[ Customer{name='null', type='VIP', total=1500, balance=2500}, Customer{name='B', type='STANDARD', total=3000, balance=3500}, Customer{name='C', type='STANDARD', total=4000, balance=4500}, Customer{name='A', type='VIP', total=3500, balance=5000}, Customer{name='A', type='STANDARD', total=1000, balance=1500} ]Puede hacerlo usando toMap usando la función de combinación, transmita la lista de clientes y recopile en Map usando name-type como clave, y use la función de combinación para resumir el total y el balance de los clientes que tienen la misma clave.
Collection<Customer> result = listCust.stream() .collect(Collectors.toMap(cu -> cu.getName() + "-" + cu.getType(), Function.identity(), (c1, c2) -> { c1.setTotal(c1.getTotal().add(c2.getTotal())); c1.setBalance(c1.getBalance().add(c2.getBalance())); return c1; })).values();