Tengo un caso de uso como este en el que necesito agregar valores en una lista por grupo múltiple pero luego calcular el porcentaje de distribución de cada uno de esos valores y crear una nueva lista.
Un ejemplo de lista de elementos:
week1 source1 destination1 100 week1 source1 destination2 200 week1 source2 destination1 200 week1 source2 destination2 100 week2 source1 destination1 200 week2 source1 destination2 200A partir de esto, quiero agrupar por semana y fuente y calcular la cantidad total y luego distribuir el porcentaje según la cantidad.
Como ejemplo, la cantidad total para la semana 1 desde la fuente 1 es 300, que va al destino 1 (100) y al destino 2 (200). Ahora el porcentaje de distribución es para la semana 1 del origen 1 al destino 1 es 33.33% y para la semana 1 del origen 1 al destino 2 al 66.66%
Por ejemplo, la salida sería:
week1 source1 destination1 33.33% week1 source1 destination2 66.66% week1 source2 destination1 66.66% week1 source2 destination2 33.33% week2 source1 destination1 50% week2 source1 destination2 50%¿Cómo puedo lograr este resultado usando flujos de Java 8?
Digamos que tengo una lista de estos objetos como Lista en el objeto "registros":
public class Record { private String sourceNode; private String destinationNode; private String weekIndex; private String quantity; } Map<String, Map<String, List<Record>>> RecordsGroupByWeekAndSource = records.stream() .collect(Collectors.groupingBy(Record::getWeekIndex, Collectors.groupingBy(Record::getSourceNode)));Esto me daría el grupo de artículos por semana y fuente. Pero tendré que iterar este mapa nuevamente para calcular la cantidad total en cada lista que reside dentro del mapa del objeto del mapa. Pero, ¿hay alguna manera de que pueda hacer este cálculo de porcentaje dentro de la colección groupingBy?
Puede crear un mapa con clave: week+source y valor como cantidad total. CollectingAndThen se puede utilizar el mapa y crear la lista resultante:
// Value Objects: @Data @AllArgsConstructor class Records { String week, source, destination; int quantity; } @Data @AllArgsConstructor class Distribution { String week, source, destination; float pctDist; public Distribution(Records r) { this.week = r.getWeek(); this.source = r.getSource(); this.destination = r.getDestination(); } } import static java.util.stream.Collectors.*; public class SO { public static void main(String[] args) { List<Record> recordList = List.of( new Record("week1", "source1", "destination1", 100), new Record("week1", "source1", "destination2", 200), new Record("week1", "source2", "destination1", 200), new Record("week1", "source2", "destination2", 100), new Record("week2", "source1", "destination1", 200), new Record("week2", "source1", "destination2", 200)); Function<Map<String, Integer>, List<Distribution>> distExtractor = totalQuantityMap -> recordList.stream().map(r -> getDistribution(r,totalQuantityMap)).collect(toList()); List<Distribution> result = recordList.stream().collect(collectingAndThen(groupingBy(r -> r.getWeek() + r.getSource(), summingInt(Record::getQuantity)), distExtractor)); // print the result result.forEach((rec) -> System.out.println(rec.week + "\t" + rec.source + "\t" + rec.destination + "\t" + rec.pctDist)); } private static Distribution getDistribution(Record r, Map<String, Integer> weekAndSourceToTotalQuantityMap) { int total = weekAndSourceToTotalQuantityMap.get(r.getWeek() + r.getSource()); float pctDist = (r.getQuantity() * 100) / total; var dist = new Distribution(r); dist.setPctDist(pctDist); return dist; } }Producción:
// Precision can be worked upon in getDistribution method week1 source1 destination1 33.0 week1 source1 destination2 66.0 week1 source2 destination1 66.0 week1 source2 destination2 33.0 week2 source1 destination1 50.0 week2 source1 destination2 50.0Puedes lograrlo usando stream dos veces:
Código de muestra:
import java.util.*; import java.util.stream.*; class Record { public String week; public String source; public String destination; public Integer qty; Record(String week, String source, String destination, Integer qty) { this.week = week; this.source = source; this.destination = destination; this.qty = qty; } } public class Main { public static void main(String[] args) { List<Record> records = new ArrayList<>(); records.add(new Record("w1", "hyd", "kur", 10)); records.add(new Record("w1", "hyd", "gwd", 20)); records.add(new Record("w2", "hyd", "kur", 40)); records.add(new Record("w2", "hyd", "gwd", 10)); Map<String, Map<String, Integer>> sums = records .stream() .collect(Collectors.groupingBy(rec -> rec.week, Collectors.groupingBy(rec -> rec.source, Collectors.summingInt(rec->rec.qty)))); records = records .stream() .map(rec -> { rec.qty = rec.qty*100 / sums.get(rec.week).get(rec.source); return rec; }).collect(Collectors.toList()); records.forEach((rec)->System.out.println(rec.week+"\t"+rec.source+"\t"+rec.destination+"\t"+rec.qty)); } }