Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

246
Vistas
Aggregating values in a list by multiple group by and calculating percentage of distribution in java 8

I have use case like this where I need to aggregate values in a list by multiple group by but then calculate percentage of distribution of each of those values and create a new list.

An example of list of items:

week1  source1  destination1   100
week1  source1  destination2   200
week1  source2  destination1   200
week1  source2  destination2   100
week2  source1  destination1   200
week2  source1  destination2   200

From this I want to group by week and source and calculate the total quantity and then distribute percentage based on quantity.

As an example the total quantity for week 1 from source 1 is 300, which is going to destination 1(100) and destination 2(200). Now the percentage of distribution is for week 1 from source 1 to destination1 is 33.33% and for week1 from source 1 to destination 2 to 66.66%

For example the output would be:

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%

How can I achieve this result using Java 8 streams.

Say I have list of these objects as List into "records" object:

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)));

This would give me the items group by week and source. But I will have to iterate this map again to calculate the total quantity in each list that resides inside map of map object. But Is there a way I can do this percenatage calcualtion within the groupingBy collection itslef?

over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

You can create a map with key: week+source and value as total quantity. CollectingAndThen can be utilize the map and create resulting list:

// 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;
 }
}

Output:

// 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.0
over 4 years ago · Santiago Trujillo Denunciar

0

You can achieve it by using stream twice:

  1. In first collection, you can do group by and do sum
  2. Stream your records again and use sum result from the fist step to calculate percentage

Sample Code:

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));
    }
}
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda