Reputation: 408
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?*)
);
}
}
Upvotes: 4
Views: 328
Reputation: 31868
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 cases Collectors.counting()
would be a good downstream
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 custom Comparator<Map.Entry<Product, Long>>
aided with an additional check over the name comparison.
Furthermore, iterating over the entries of this map, while sort
ing and limit
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 a max
value of the price from all the orders of each customer.
So a comparator such as Comparator.comparing(e -> findHighestValueOrder(e.getValue())
, where the e.getValue()
is a List<Order> customerOrders
would help you with the solution.
I would leave the implementation of findHighestValueOrder
up to you, since if you know how to map
and flatMap
, you just need to find the max
price amongst these orders or fallback to some default value such as BigDecimal.ZERO
.
Upvotes: 6