Java 8 Stream groupingBy with custom logic

帅比萌擦擦* 提交于 2021-02-16 04:42:04

问题


I have a list of Records. Which has two fields: LocalDateTime instant and a Double data.

I want to groupBy all the records by Hour and create a Map<Integer, Double>. Where the keys (Integer) are hours and values(Double) are last Data of that hour - first Data of that hour.

What I have done so far is following:

Function<Record, Integer> keyFunc = rec->rec.getInstant().getHour();
Map<Integer, List<Record>> valueMap = records.stream().collect(Collectors.groupingBy(keyFunc));

I want the value map to hold Double instead of List<Records>.

For Example: List records can be following:

Instant            Data
01:01:24           23.7
01:02:34           24.2
01:05:23           30.2
...
01:59:27           50.2
02:03:23           54.4
02:04:23           56.3
...
02:58:23           70.3
...

etc

Resulting map should be:

Key       Value
1          26.5 (50.2-23.7)
2          15.9 (70.3-54.4)
...

回答1:


You are mostly looking for Collectors.mapping within the groupingBy.

Map<Integer, List<Double>> valueMap = records.stream()
        .collect(Collectors.groupingBy(keyFunc, 
                Collectors.mapping(Record::getData, Collectors.toList())));

This would group Records by their instant's hour and corresponding data for such records into a List as values of the map. Based on comments further

I want to subtract the first data from the last data

Yes the list will be sorted list based on instant

you can use the grouped map to get the desired output as:

Map<Integer, Double> output = new HashMap<>();
valueMap.forEach((k, v) -> output.put(k, v.get(v.size() - 1) - v.get(0)));

Alternatively, you could use Collectors.mapping with Collectors.collectingAndThen further as:

Map<Integer, Double> valueMap = records.stream()
        .collect(Collectors.groupingBy(keyFunc,
                Collectors.mapping(Record::getData, 
                        Collectors.collectingAndThen(
                                Collectors.toList(), recs -> recs.get(recs.size() - 1) - recs.get(0)))));



回答2:


You can use collectingAndThen as a downstream collector to groupingBy, and use the two extreme values of each group to compute the difference:

Map<Integer, Double> result = records.stream()
    .collect(
        Collectors.groupingBy(rec -> rec.getInstant().getHour(),

        Collectors.collectingAndThen(
                Collectors.toList(), 
                list -> {
                    //please handle the case of 1 entry only
                    list.sort(Comparator.comparing(Record::getInstant));

                    return list.get(list.size() - 1).getData() 
                           - list.get(0).getData();
                })));

Collectors.groupingBy(rec -> rec.getInstant().getHour() will group entries by hour. As used here, Collectors.collectingAndThen will take hourly entries as lists, sort every such list by the instant field then find the difference between the two extreme elements.




回答3:


Based on the comment that the list would be sorted on timestamp, the following would work :

    Map<Integer, Double> valueMap = records.stream()
            .collect(Collectors.groupingBy(rec -> rec.getInstant().getHour(),
                    Collectors.mapping(Record::getData,
                        Collectors.collectingAndThen(Collectors.toList(),recs -> recs.get(recs.size()-1) - recs.get(0)))));


来源:https://stackoverflow.com/questions/53967280/java-8-stream-groupingby-with-custom-logic

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!