В Java 8, как я могу отфильтровать список объектов путем группировки и суммирования? - PullRequest
0 голосов
/ 18 июня 2019

Допустим, у меня есть список объектов:

objList
Integer id Double price String userCode
         1         10.5             AAA
         2         9.5              AAA
         3         10.0             AAA
         4         10.0             BBB
         5         10.0             BBB

и я хочу изменить список, чтобы я мог группировать по userCode и суммировать цены, например:

 objList
    Integer id Double price String userCode
             1         30.0             AAA
             4         20.0             BBB

Как мне добиться этого в Java 8 с помощью лямбды? Если возможно, я бы хотел добиться этого без создания нового списка и без потери других полей объекта (как в этом случае, идентификатор). Я уже видел другие ответы на этот вопрос, но каждый второй ответ необходим для создания нового списка или коллекции.

Я уже пробовал это:

Collection<Obj1> objListSorted = objList.stream().collect(
                    toMap(
                            f -> f.getId(),
                            Function.identity(),
                            (s, a) -> new Obj1(
                                        (int) s.getId(), 
                                        s.getPrice() + a.getPrice(),
                                        s.getUserCode()
                                        ))
                          )
                    .values();      

Но на самом деле я работаю с модельным классом, который имеет более 80 полей, поэтому я хотел бы добиться этого, не создавая новый экземпляр Obj1. Я хочу сохранить первый идентификатор в группе, а также сохранить все остальные поля объекта такими, какие они есть. Также, если возможно, я бы хотел получить окончательный результат в виде списка, а не коллекции.

Ответы [ 2 ]

1 голос
/ 18 июня 2019

Попробуйте это

РЕШЕНИЕ 1

Один из способов сделать это -

List<Pricing> allPricings = getAllPricings();

List<Pricing> result = allPricings.stream()
        .collect(Collectors.groupingBy(Pricing::getUserCode))
        .entrySet().stream()
        .map(e -> e.getValue().stream()
                .reduce((f1,f2) -> new Pricing(f1.getId(),f1.getPrice() + f2.getPrice(),f1.getUserCode())))
        .map(f -> f.get())
        .collect(Collectors.toList());

РЕШЕНИЕ 2

Это более компактный способ

Collection<Pricing> result2 = 
        allPricings.stream()
                .collect(groupingBy(Pricing::getUserCode, 
                        collectingAndThen(
                                reducing((p1, p2)-> new Pricing(p1.getId(),p1.getPrice() + p2.getPrice(),p2.getUserCode())), Optional::get)))
                .values();

РЕШЕНИЕ 3

Если вы не хотите создавать объект, попробуйте следующий. Но одним из побочных эффектов этой реализации является то, что она изменяет исходное содержимое списка, поэтому лучше не использовать исходный список, а использовать клонированный.

Collection<Pricing> result3 =
        allPricings.stream()
                .collect(groupingBy(Pricing::getUserCode,
                        collectingAndThen(
                                reducing((p1, p2)-> {
                                    p1.setPrice(p1.getPrice() + p2.getPrice());
                                    return p1;
                                }), Optional::get)))
                .values();

Пример функции для getAllPricings

private static List<Pricing> getAllPricings() {
    List<Pricing> pricings = new ArrayList<>();
    pricings.add(new Pricing(1, 10.5, "AAA"));
    pricings.add(new Pricing(2, 9.5, "AAA"));
    pricings.add(new Pricing(3, 10.0, "AAA"));
    pricings.add(new Pricing(4, 10.0, "BBB"));
    pricings.add(new Pricing(5, 10.0, "BBB"));
    return pricings;
}

Результат

[
  {
    "id": 1,
    "price": 30,
    "userCode": "AAA"
  },
  {
    "id": 4,
    "price": 20,
    "userCode": "BBB"
  }
]
1 голос
/ 18 июня 2019

Вы можете использовать groupingBy с нижестоящим Collector.Вы можете использовать reducing для добавления цен, однако это возвращает Optional, который вам не нужен, но он гарантированно присутствует, поэтому вы можете просто get() его результат.

Collection<Obj1> added = objList.stream()
       .collect(groupingBy(Obj1::getUserCode),
                collectingAndThen(
                    reducing((o1, o2) -> { 
                        // not creating another instance, but: side effect!
                        o1.setPrice(o1.getPrice()+o2.getPrice()); 
                        return o1; 
                    }),
                    opt -> opt.get()
                ))
       .values();

Нет смысла требовать, чтобы это стало List, поскольку в созданных значениях нет упорядочения, поэтому любое добавление будет произвольным.Конечно, вы можете сделать

List<Obj1> asList = new ArrayList<>(added);

и получить свой List.Если вы хотите упорядочить результат, вы можете выполнить groupingBy, используя TreeMap и соответствующий Comparator, это гарантирует, что результат values() также будет отсортирован по этому (вам все равно нужно будет сделатьэто List хотя).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...