I am trying to figure out how to calculate the percentage of a specific value in a list when using stream
.
My object getTag
can take value G
or Y
and I want to calculate the percentage of G:s
in the list. The other object is getDateTime
which is of the form 1946-01-12
.
I have the condition filter(weather -> !weather.getDateTime().isAfter(dateTo) && !weather.getDateTime().isBefore(dateFrom))
because I only want the percentage between two dates that are user input.
So each DateTime value corresponds to either G
or Y
. I used Collectors.counting()
to calculate the frequency of both G
and Y
, but how can I get the percentage?
Map<String, Long> percentage = weatherData.stream()
.filter(weather -> !weather.getDateTime().isAfter(dateTo) && !weather.getDateTime().isBefore(dateFrom))
.collect(Collectors.groupingBy(Weather::getTag, Collectors.counting()));
If you map the item you care about ("G") to 1, and everything else to 0, the average value is the percentage of "G" in the stream.
double pctG = list.stream()
.mapToInt(obj -> obj.getTag().equals("G") ? 1 : 0)
.summaryStatistics()
.getAverage();
With Java 13, you can use the teeing()
collector to count the elements by tag, and the total number of elements after filtering, finishing by dividing the group count by the total:
Map<String, Double> fractions = weatherData.stream()
.filter(...)
.collect(
Collectors.teeing(
Collectors.groupingBy(Weather::getTag, Collectors.counting()),
Collectors.counting(),
YourClass::scale));
Where the scale()
function divides each group by the total:
static <T> Map<T, Double> scale(Map<? extends T, Long> counts, long total) {
return counts.entrySet().stream().
.collect(Collectors.toMap(e -> e.getKey(), ((double) e.getValue()) / total));
}