Java 8: AveragingDouble и сопоставление с объектом - PullRequest
3 голосов
/ 05 мая 2020

У меня есть список объектов транзакции, как показано ниже:

List<Transaction> transactions = Arrays.asList(
                new Transaction(brian, 2011, 300, "X", "M1"),
                new Transaction(raoul, 2012, 1000, "T", "M1"),
                new Transaction(raoul, 2011, 400, "S", "M2"),
                new Transaction(mario, 2012, 710, "X", "M1"),
                new Transaction(mario, 2012, 700, "X", "M2"),
                new Transaction(alan, 2012, 950, "T", "M1")
        );

Класс транзакции:

class Transaction {
    private final Trader trader;
    private final int year;
    private final int value;
    private String type;
    private String method;
}

Теперь мне нужно найти среднее значение в зависимости от типа транзакции для каждой группы и, наконец, выгрузить среднее значение в объект Result с некоторыми дополнительными данными о группе, среднее значение которой вычисляется et c.

перед вычислением. в списке выполняются следующие операции:

  • группировка транзакции по году
  • затем группировка по методу

и конечный созданный объект должен иметь среднее значение, год и значение метода в нем.

Класс результата:

class Result {
    private Double avg;
    private int year;
    private String method;
}

Код:

 Map<Integer, Map<String, Result>> res = transactions.stream().collect(
                groupingBy(Transaction::getYear,
                        groupingBy(Transaction::getMethod),
                            collectingAndThen( averagingDouble( t -> "X".equals(t.getType()) ? 1: 0 ),
                                    v -> new Result(v, GROUP_METHOD? , GROUP_YEAR?))
                )
        );

Но значения GROUP_METHOD и GROUP_YEAR здесь недоступны в зависимости от группировки выполняется, поскольку averagingDouble() производит двойное, и вся остальная информация теряется в этом сопоставлении.

есть ли способ получить эти поля или правильно отобразить результат в объект?

Ответы [ 2 ]

3 голосов
/ 06 мая 2020

Вы можете сделать это:

После группировки по год и метод с mapping сборщиком создайте объект Result как Collectors.mapping(t->new Result(...),), как вы знаете mapping сборщик принимает collector в качестве второго аргумента. Поскольку вы хотите объединить их после группировки и вычислить среднее значение, сборщик collectingAndThen - лучший выбор для выполнения этого здесь. действительно, collectingAndThen после сбора в список принимает функцию финишера (функция среднего) и вычисляет среднее значение для всех элементов в списке.

transactions.stream().collect(
      groupingBy(Transaction::getYear,
           groupingBy(Transaction::getMethod,
               mapping(t -> new Result((double) t.getValue(), t.getYear(), t.getMethod()),
                        collectingAndThen(Collectors.toList(), average::apply)))));

, а средняя функция:

 Function<List<Result>, Result> average = l -> new Result(l.stream()
            .mapToDouble(Result::getAvg)
            .average().orElse(0d), l.get(0).getYear(), l.get(0).getMethod());
1 голос
/ 06 мая 2020

Вы можете использовать averagingDouble для сбора в Map<Integer, Map<String, Double>>, как в OP:

Map<Integer, Map<String, Double>> doubleMap = transactions.stream()
                .collect(groupingBy(Transaction::getYear,
                        groupingBy(Transaction::getMethod,
                                averagingDouble(Transaction::getValue))));

Затем переназначить Map<Integer, Map<String, Result>>:

Map<Integer, Map<String, Result>> resultMap = doubleMap.entrySet().stream()
      .flatMap(my -> my.getValue().entrySet().stream()
             .map(mm -> new Result(mm.getValue(), my.getKey(), mm.getKey())))
      .collect(groupingBy(Result::getYear, toMap(Result::getMethod, Function.identity())));

или в одном операторе используя collectingAndThen:

 Map<Integer, Map<String, Result>> collect1 = transactions.stream()
     .collect(collectingAndThen(
         groupingBy(Transaction::getYear,
            groupingBy(Transaction::getMethod,averagingDouble(Transaction::getValue))),
         map -> map.entrySet().stream()
                 .flatMap(my -> my.getValue().entrySet().stream()
                    .map(mm -> new Result(mm.getValue(), my.getKey(), mm.getKey())))
                 .collect(groupingBy(Result::getYear, 
                                     toMap(Result::getMethod, Function.identity())))
        ));

вывод:

{2011={M1=Result[avg=300.0, year=2011, method='M1'], M2=Result[avg=400.0, year=2011, method='M2']},
 2012={M1=Result[avg=886.6666666666666, year=2012, method='M1'], M2=Result[avg=700.0, year=2012, method='M2']}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...