Calculate the percentage of value using Collection framework



I have List of TrainingRequest where each and every element has List of Feedback.

@Data
class TrainingRequest{
    @Transient
    List<Feedack> feedback;
}

@Data
class Feedback{
    String Q1;
    String Q2;
}

I need to get all given result of Q1,Q2 and calculate percentage of each value.

List<TrainingRequest> trainingList = Optional.ofNullable(trainingRequestList).orElseGet(Collections::emptyList)
                                    .stream().map(m -> {
                                        List<Feedback> feedback = findByTrainingRequestId(m.getId());
                                        m.setFeedback(feedback);  // assigning Feedack to TrainingRequest
                                        return m;
                                    }).collect(Collectors.toList());

To flat all the feedback

List<Feedback> flatMap = trainingList.stream().flatMap(f -> f.getFeedback().stream()).collect(Collectors.toList());

To calculate each value of Q1 and Q2, I’m grouping it and getting the count. I need to get the percentage of each Q1, Q2 value insted of count.

Map<String, Map<String, Long>> map = new TreeMap<>();

map.put("Q1", flatMap.stream().collect(Collectors.groupingBy(Feedback::getQ1, Collectors.counting())));
map.put("Q2", flatMap.stream().collect(Collectors.groupingBy(Feedback::getQ2, Collectors.counting())));

When I use Collectors.counting(), it’s giving the following output:

{
  "Q1": {
    "unsatisfied": 2,
    "Satisfied": 1,
    "satisfied": 1
  },
  "Q2": {
    "Yes": 4
  }
}

But I need it to give percentage as I expected

{
  "Q1": {
    "unsatisfied": 50 %,
    "Satisfied": 25 %,
    "satisfied": 25 %
  },
  "Q2": {
    "Yes": 100 %
  }
}

How to do it in a efficient way? Do I need to optimize the above code?

Answer

This might not be an optimized answer but you can get the result. Create a map to keep total values for each Q, and then use it to calculate percentage,

Map<String, Long> totalCountMap = map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().values().stream().reduce(Long::sum).orElse(0l)));

Map<String, Map<String, Long>> result = map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e1 -> (e1.getValue() * 100 / totalCountMap.get(e.getKey()))))));


Source: stackoverflow