I have a list of SensorSample POJOs
public class SensorSample {
private Device.SensorType sensorType; // This is an enum
private double sample;
private long timestamp;
// Constructor
// Setters
// Getters
}
I need to group them by the timestamp, so that all the SensorSamples of the same day are together. Then I need to reduce them so that I have only one SensorSample for each day and the value of its sample is the average of the value of the sample of all the objects for that day. Is there a way to do that with Streams?
So far I got this to group them together:
Map<Long, List<SensorSample>> yearSamples = samples.stream()
.collect(groupingBy(sample -> SECONDS_IN_A_DAY*Math.floorDiv(sample.getTimestamp(), SECONDS_IN_A_DAY)));
But I don't know how to go any further.
Something like this, I think. To find the average number of a group:
Map<Long, Double> averages = samples.stream()
.collect(groupingBy(SensorSample::getTimestamp,
averagingDouble(SensorSample::getSample)));
I didn't expand out your formula for the day, I think it's more readable if I just called getTimestamp and leave the details out. Your code might also be more readable if you added a getDay method to SensorSample.
Also this would be easier to test if you provided an MCVE, as it's a little hard to test the code above with only one partial class to go on.
it seems as if you want a List<SensorSample> as a result where each group after the groupingBy is reduced into a single SensorSample.
List<SensorSample> result = samples.stream()
.collect(groupingBy(sample -> SECONDS_IN_A_DAY*Math.floorDiv(sample.getTimestamp(), SECONDS_IN_A_DAY))
.entrySet()
.stream()
.map(e -> {
SensorSample sensorSample = new SensorSample();
sensorSample.setTimestamp(e.getKey());
double average = e.getValue().stream()
.mapToDouble(SensorSample::getSample)
.average().orElse(0);
sensorSample.setSample(average);
sensorSample.setSensorType(e.getValue().get(0).getSensorType());
return sensorSample;
}).collect(Collectors.toList());
The map logic seems a little bit large thus I'd consider refactoring it out to a method as such:
private static SensorSample apply(Map.Entry<Long, List<SensorSample>> e) {
SensorSample sensorSample = new SensorSample();
sensorSample.setTimestamp(e.getKey());
double average = e.getValue().stream()
.mapToDouble(SensorSample::getSample)
.average().orElse(0);
sensorSample.setSample(average);
sensorSample.setSensorType(e.getValue().get(0).getSensorType());
return sensorSample;
}
Then the stream pipeline would become:
List<SensorSample> result = samples.stream()
.collect(groupingBy(sample -> SECONDS_IN_A_DAY*Math.floorDiv(sample.getTimestamp(), SECONDS_IN_A_DAY))
.entrySet()
.stream()
.map(Main::apply)
.collect(Collectors.toList());
Where Main is the class containing the apply method.
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