Group by, sum then sort a list of transaction objects java

Viewed 608

I have a list of transactions by day, the transaction contains the following attributes:

Transaction(int transactionID,
                       DateTime transactionDate,
                       String shopId,
                       int productReference,
                       int quantity,
                       float price);

Having a list List<Transaction>, I want to extract top 100 selled products by shop.

So I need to group transactions by shopId then by productReference, then summing quantities than sorting from most selled to least.

Thanks you for your Help

2 Answers
private static Collector<Transaction, ?, List<Transaction>> limit(int limit) {
    return Collector.of(
            ArrayList::new,
            (list, transaction) -> { if (list.size() < limit) list.add(transaction); },
            (list1, list2) -> {
                list1.addAll(list2.subList(0, Math.min(list2.size(), Math.max(0, limit - list1.size()))));
                return list1;
            }
    );
}



  public static void main(String[] args) {
        Map<String, List<Transaction>> groupedMap = listOfTransactions
                .stream()
                .sorted((t1, t2) -> Integer.compare(t2.getQuantity(), t1.getQuantity()))
                .collect(
                Collectors.groupingBy(
                        Transaction::getShopId,
                        limit(100)
                )
        );
    }

As a result you'll get a map with shopId as a key, and lists of transactions sorted by quantity as a value. Is it expected behavior?

I'd suggest using of additional Product type, with overridden equals() and hasCode() which will consist only of shopId and productReference . New type will serve as an output, which will make all the transformation job more obvious. Consider my version, with Lombok lib usage:

import lombok.*;

@Data
@RequiredArgsConstructor(staticName = "of")
@ToString
public class Product {

    final String shopId;
    final int productReference;
}

and the function code itself:

    List<Product> products = transactions.stream()
            // grouping transactions by the same product
            .collect(Collectors.groupingBy(transaction -> Product.of(
                    transaction.getShopId(),
                    transaction.getProductReference())))
            .entrySet().stream()
            // summing all price * quantity occurrences to find top sellings
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    e -> e.getValue().stream()
                            .mapToDouble(p -> p.getQuantity() * p.getPrice())
                            .sum()))
            .entrySet().stream()
            // sorting by the most expensive ones at the top, 
            // limiting to 10 and collecting to the list 
            .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
            .map(Map.Entry::getKey)
            .limit(10)
            .collect(Collectors.toList());
Related