Группировка Java 8 Stream с помощью пользовательской логики - PullRequest
0 голосов
/ 29 декабря 2018

У меня есть список Records.Который имеет два поля: LocalDateTime instant и Double data.

Я хочу сгруппировать все записи по часам и создать Map<Integer, Double>.Где ключи (Integer) - это часы, а значения (Double) - последние данные этого часа - первые данные этого часа.

То, что я сделал до сих пор, следующее:

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

Я хочу, чтобы карта значений содержала Double вместо List<Records>.

Например: Список записей может быть следующим:

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
...

и т. Д.

Результирующая карта должнабыть:

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

Ответы [ 3 ]

0 голосов
/ 29 декабря 2018

Вы можете использовать collectingAndThen в качестве нисходящего коллектора для groupingBy и использовать два крайних значения каждой группы для вычисления разницы:

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() сгруппирует записи по часам.Как используется здесь, Collectors.collectingAndThen будет принимать ежечасные записи в виде списков, отсортировать каждый такой список по полю instant, а затем найти разницу между двумя крайними элементами.

0 голосов
/ 29 декабря 2018

На основании комментария о том, что список будет отсортирован по метке времени, будет работать следующее:

    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)))));
0 голосов
/ 29 декабря 2018

Вы в основном ищете Collectors.mapping в пределах groupingBy.

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

Это группировало бы Record s по часам instant и соответствующим данным.для таких записей в List как значения карты.На основе комментариев далее

Я хочу вычесть первые данные из последних данных

Да, список будет отсортирован по списку мгновенных

васможно использовать сгруппированную карту для получения желаемого результата в виде:

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

В качестве альтернативы вы можете использовать Collectors.mapping с Collectors.collectingAndThen далее как:

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)))));
...