Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 936
varman Avatar asked Apr 23 '20 12:04

varman


People also ask

How do you find the percentage of collections?

Add up your total collections and divide by adjusted charges (charges less contractual adjustments) to determine how much you have actually collected. For example, if your practice only collected $50 on a procedure contracted for $75, your net collection rate would be 67 percent (50 divided by 100 minus 25).

How do you calculate percentage in calculated field in tableau?

To calculate percentages in your visualization: Select Analysis > Percentages Of, and then select a percentage option.

How do I calculate percentage in Java?

Percentage = (Obtained score x 100) / Total Score To get these parameters (inputs) from the user, try using the Scanner function in Java.


1 Answers

Your question was a bit unclear, so I tried to simplify the logic a bit for myself. I came up with a snipit to calculate the percentage of even/odd integers in an IntStream (which is not so different than what you're trying to do).

IntStream.range(0, 101).boxed()
         .collect(Collectors.groupingBy(integer -> (integer % 2) == 0 ? "even" : "odd",
             Collectors.collectingAndThen(Collectors.counting(), aLong -> aLong + " %")));

Notice the use of the collectingAndThen() this let's us first collect the values, then map the result into another value using a mapper/finisher.

In your case, this would be translated into something like this

map.put("Q1", flatMap.stream().collect(Collectors.groupingBy(Feedback::getQ1,
Collectors.collectingAndThen(Collectors.counting(), count -> (count / flatMap.size()) * 100.00 + " %")));

map.put("Q2", flatMap.stream().collect(Collectors.groupingBy(Feedback::getQ2,
Collectors.collectingAndThen(Collectors.counting(), count -> (count / flatMap.size()) * 100.00 + " %")));

UPDATE

Since you specifically asked about optimization, here are a couple of points to that

1. Don't create a new collection when you can reuse the existing one

// this code is unnecessarily creating a new collection
List<TrainingRequest> trainingList = Optional.of(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());

it could be simplified to this

// to avoid NullPointerExceptions
trainingRequestList = trainingRequestList == null ? Collections.emptyList() : trainingRequestList;
// because java is pass by reference we are able to do this
trainingRequestList.forEach(m -> m.setFeedback(findByTrainingRequestId(m.getId())));

2. Don't Collect if you are going to stream the collection again

// to hold the count of Q1 an Q2
final Map<String, Integer> count = new HashMap<>();

//  Order(n), n = trainingRequests count
trainingRequestList.forEach(trainingRequest -> {
   List<Feedback> feedbacks = findByTrainingRequestId(trainingRequest.getId());
   //  Order(m), m = feedbacks count
   feedbacks.forEach(f -> {
     count.merge("Q1", f.getQ1(), Integer::sum);
     count.merge("Q2", f.getQ2(), Integer::sum);
   });
   trainingRequest.setFeedback(feedbacks);
}

// finally we can collect the percentage
// Order(1)
int totalCountOfFeedbacks = count.values().stream().mapToInt(Integer::intValue).sum();
Map<String, String> result = count.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> 100.00 * (entry.getValue() / totalCountOfFeedbacks ) + " %"));

Notice that these optimizations will not affect the fact that your logic is currently Order(n * m), it would be difficult to provide you further hints without actually looking at the code.

like image 162
Ahmed Sayed Avatar answered Sep 30 '22 12:09

Ahmed Sayed