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 gooddownstream
to make use of.Note that it would provide you
Map<Product, Long> productFrequency
as 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
sort
ing andlimit
ing 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
toList
downstream.It would aid you with
Map<Customer, List<Order>>
, amongst which you would need to find the entry with amax
value 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> customerOrders
would help you with the solution.I would leave the implementation of
findHighestValueOrder
up to you, since if you know how tomap
andflatMap
, you just need to find themax
price amongst these orders or fallback to some default value such asBigDecimal.ZERO
.