Home > Mobile >  Stream group, transform and sort events
Stream group, transform and sort events

Time:04-24

I have a series of events, each event contains a timestamp, device, serialnumber, and measurement.

class Event {
    private String device;
    private String description;
    private String serialnumber;
    private Measurement measurement;
}

class Measurement {
    private LocalDateTime timestamp;
    private int value;
}

I have a stream of these events, and I would like to aggregate them into a simpler structure, dropping the serial number, then group them by device, and then sorting the measurements by timestamp and value.

{device: "device_1", description: "first device", serialnumber: "1", measurement: { timestamp: 2022-04-23T18:20:22Z, value: 180}}
{device: "device_2", description: "second device", serialnumber: "2", measurement: { timestamp: 2022-04-23T18:20:28Z, value: 120}}
{device: "device_2", description: "second device", serialnumber: "2", measurement: { timestamp: 2022-04-23T18:20:20Z, value: 160}}
{device: "device_1", description: "first device", serialnumber: "1", measurement: { timestamp: 2022-04-23T18:20:22Z, value: 170}}
[
    {
        device: "device_1",
        description: "first device",
        measurements: [
            { timestamp: 2022-04-23T18:20:22Z, value: 170},
            { timestamp: 2022-04-23T18:20:22Z, value: 180}
        ]
    },
    {
        device: "device_2",
        description: "second device",
        measurements: [
            { timestamp: 2022-04-23T18:20:20Z, value: 160},
            { timestamp: 2022-04-23T18:20:28Z, value: 120}
        ]
    }
]

I've managed to get the required format by creating a "builder" class in which you can insert events that get then processed and added to the data members in the correct format/order. However, I think it would be nicer to somehow achieve this on the fly without another extra class, but using the groupingBy and toMap (and other?) stream methods.

If anyone could point me in the right direction or provide some examples I would be very thankful!

EDIT: I added the description that should be included in the final output.

CodePudding user response:

I've come up with this solution assuming that for each device there cannot be multiple measurements with same timestamp and value.

I understand that this could be a limit, but since I cannot see any ways of sorting a subset collected with a Collectors, I've decided to use a TreeSet to store the grouped measurements so that they will be sorted according to their natural ordering.

class Event {
    private String device;
    private String description;
    private String serialnumber;
    private Measurement measurement;

    public Event(String device, String description, String serialnumber, Measurement measurement) {
        this.device = device;
        this.description = description;
        this.serialnumber = serialnumber;
        this.measurement = measurement;
    }

    //... getters and setters ...
}

class Measurement implements Comparable<Measurement> {
    private LocalDateTime timestamp;
    private int value;

    public Measurement(LocalDateTime timestamp, int value) {
        this.timestamp = timestamp;
        this.value = value;
    }

    //... getters and setters ...

    //Redefinition of the natural ordering by timestamp and value

    @Override
    public int compareTo(Measurement m) {
        Comparator<Measurement> cmp = Comparator.comparing(Measurement::getTimestamp).thenComparing(Measurement::getValue);
        return cmp.compare(this, m);
    }

    public String toString(){
        return String.format("%s - %d", timestamp, value);
    }
}

public class Test {

    public static void main(String[] args) {
        List<Event> dataSource = new ArrayList<>(List.of(
                new Event("device_1", "first device", "1", new Measurement(LocalDateTime.parse("2022-04-23T18:20:22"), 180)),
                new Event("device_2", "second device", "2", new Measurement(LocalDateTime.parse("2022-04-23T18:20:28"), 120)),
                new Event("device_2", "second device", "2", new Measurement(LocalDateTime.parse("2022-04-23T18:20:20"), 160)),
                new Event("device_1", "first device", "1", new Measurement(LocalDateTime.parse("2022-04-23T18:20:22"), 170))));

        Map<String, TreeSet<Measurement>> map = dataSource.stream()
                .collect(Collectors.groupingBy(Event::getDevice, Collectors.mapping(Event::getMeasurement, Collectors.toCollection(TreeSet::new))));

        for (String key: map.keySet()){
            System.out.printf("%s => %s%n", key, map.get(key));
        }
    }
}

CodePudding user response:

You could get a Map<String, List<Measurement>> using groupingBy if that is what you are looking for:

Map<String, List<Measurement>> result = 
        events.stream()
                .collect(Collectors.groupingBy(Event::getDevice,
                         Collectors.mapping(Event::getMeasurement, Collectors.toList())));
  • Related