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?
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).
To calculate percentages in your visualization: Select Analysis > Percentages Of, and then select a percentage option.
Percentage = (Obtained score x 100) / Total Score To get these parameters (inputs) from the user, try using the Scanner function in Java.
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 + " %")));
Since you specifically asked about optimization, here are a couple of points to that
// 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())));
// 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With