Saltar al contenido

Java Streams: agrupar por dos criterios que suman el resultado

Nuestro equipo redactor ha estado largas horas buscando para dar espuestas a tus preguntas, te brindamos la respuesta así que nuestro objetivo es resultarte de gran ayuda.

Solución:

¿Hay alguna forma de resolver la tarea anterior en una secuencia?

Depende de lo que quieras decir con “en una secuencia”. Desea realizar una operación de reducción que probablemente se caracterice mejor como una combinación de una secuencia de reducciones:

  • agrupar los pedidos por mes
  • dentro de cada grupo mensual, agregue los pedidos de cada cliente para obtener una cantidad total
  • entre cada grupo mensual de resultados agregados por cliente, elija el de mayor monto (nota: no bien definido en caso de empate)

Desde la perspectiva de la API de flujo, realizar cualquiera de esas reducciones individuales en un flujo es una operación de terminal en ese flujo. Puede procesar el resultado con una nueva secuencia, incluso encadenándola sintácticamente, pero aunque eso podría tomar la forma sintáctica de una sola cadena de invocaciones de métodos, no constituiría todas las operaciones que suceden en una sola secuencia.

También puede crear un único Collector (o los componentes de uno) para que obtenga el resultado directamente recopilando el flujo de sus elementos de entrada, pero internamente, ese recopilador aún necesitaría realizar las reducciones individuales, ya sea creando y consumiendo secuencias adicionales internamente, o realizando las mismas tareas a través de API que no son de secuencia. Si cuenta esas operaciones internas, nuevamente, no, no constituiría realizar operaciones en una sola secuencia. (Pero si no considera esas reducciones internas, entonces sí, esto lo hace todo en una sola transmisión).

Intenta usar groupingBy, summingLong y comparingLong como se muestra a continuación

Map topBuyers = orders.stream()
    .collect(Collectors.groupingBy(Order::getOrderMonth,
             Collectors.groupingBy(Order::getCustomer,
             Collectors.summingLong(Order::getAmount))))
    .entrySet().stream()
    .collect(Collectors.toMap(Map.Entry::getKey,
             order -> order.getValue().entrySet().stream()
            .max(Comparator.comparingLong(Map.Entry::getValue))
            .map(cust -> new BuyerDetails(cust.getKey(), cust.getValue())).get()));

Producción


  "MARCH":  "customer": "Dan", "amount": 300 , 
  "APRIL":  "customer": "Jenny", "amount": 550 

Tiene un flujo anidado, por lo que no es un flujo y devuelve Map>.


orders.stream()
        .collect(
            Collectors.groupingBy(Order::getOrderMonth,
                Collectors.collectingAndThen(
                        Collectors.groupingBy(
                                Order::getCustomer,
                                Collectors.summarizingLong(Order::getAmount)
                        ),
                        e -> e.entrySet()
                                .stream()
                                .map(entry -> new BuyerDetails(entry.getKey(), entry.getValue().getSum()))
                                .max(Comparator.comparingLong(BuyerDetails::getAmount))
                )
            )
        )

entonces hay 3 pasos:

  • Agrupar por mes Collectors.groupingBy(Order::getOrderMonth,
  • Agrupar por nombre de cliente y sumar el monto total del pedido Collectors.groupingBy(Order::getCustomer, Collectors.summarizingLong( Order::getAmount))
  • filtrando el resultado intermedio y dejando solo a los clientes con la cantidad máxima max(Comparator.comparingLong(BuyerDetails::getAmount))

la salida es


  APRIL = Optional [ BuyerDetails  customer = 'Jenny', amount = 550  ],
  MARCH = Optional [ BuyerDetails  customer = 'Dan', amount = 300  ]

También tengo curiosidad por saber si esto se puede hacer sin flujo adicional.

Te invitamos a añadir valor a nuestra información tributando tu experiencia en las explicaciones.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *