I am facing a tricky situation where I have to use groupingBy on an object which has nested lists. I tried few things with map(), flatmap(), toMap() but not able to come up with a solution and just going in circles. Any help here from the stream experts very much appreciated. I need to implement 2 methods in my OrdersAnalyzer class. Here is how my objects look like:
public class Order {
private final String id;
private final Set<OrderLine> orderLines = new HashSet<>();
private final Customer customer;
//getters, setters, equals, hashcode omitted for brevity
}
public class OrderLine {
private final Product product;
private final Integer quantity;
//getters, setters, equals, hashcode omitted for brevity
}
public class Product {
private final String name;
private final BigDecimal price;
//getters, setters, equals, hashcode omitted for brevity
}
public class OrdersAnalyzer {
/**
* Should return at most three most popular products. Most popular product is the product that have the most occurrences
* in given orders (ignoring product quantity).
* If two products have the same popularity, then products should be ordered by name
*
* @param orders orders stream
* @return list with up to three most popular products
*/
public List<Product> findThreeMostPopularProducts(Stream<Order> orders) {
orders.forEach(order -> {
order.getOrderLines().stream().collect(
Collectors.groupingBy(OrderLine::getProduct, *What to add here?* )
);
});
}
/**
* Should return the most valuable customer, that is the customer that has the highest value of all placed orders.
* If two customers have the same orders value, then any of them should be returned.
*
* @param orders orders stream
* @return Optional of most valuable customer
*/
public Optional<Customer> findMostValuableCustomer(Stream<Order> orders) {
orders.collect(
Collectors.groupingBy(Order::getCustomer, *What to add here?*)
);
}
}
findThreeMostPopularProducts
The API that you are looking to support needs you to create a frequency map of products to lookup as you find the
top-N. In such casesCollectors.counting()would be a gooddownstreamto make use of.Note that it would provide you
Map<Product, Long> productFrequencyas a resultant, and then you would need to define a customComparator<Map.Entry<Product, Long>>aided with an additional check over the name comparison.Furthermore, iterating over the entries of this map, while
sorting andlimiting to N elements shall get you closer to the answer that you are looking for.findMostValuableCustomer
In this API, you are looking to compare the customers based on the highest values across their respective orders. So the grouping is sufficient with the default
toListdownstream.It would aid you with
Map<Customer, List<Order>>, amongst which you would need to find the entry with amaxvalue of the price from all the orders of each customer.So a comparator such as
Comparator.comparing(e -> findHighestValueOrder(e.getValue()), where thee.getValue()is aList<Order> customerOrderswould help you with the solution.I would leave the implementation of
findHighestValueOrderup to you, since if you know how tomapandflatMap, you just need to find themaxprice amongst these orders or fallback to some default value such asBigDecimal.ZERO.