I have a list of Orders I should group by two criteria.
Order_Id| Customer | Date | Amount |
1 | "Sam" | 2019-03-21 | 100 |
2 | "Nick" | 2019-03-21 | 102 |
3 | "Dan" | 2019-03-21 | 300 |
4 | "Sam" | 2019-04-21 | 400 |
5 | "Jenny" | 2019-04-21 | 220 |
6 | "Jenny" | 2019-04-12 | 330 |
Top buyer for each month by summed amount should be found, for current example:
{
MARCH: { customer='Dan', amount=300 },
APRIL: { customer='Jenny', amount=550 }
}
There is a solution I was able to find:
public class Main {
public static void main(String[] args) {
List<Order> orders = List.of(
new Order(1L, "Sam", LocalDate.of(2019, 3, 21), 100L),
new Order(2L, "Nick", LocalDate.of(2019, 3, 21), 102L),
new Order(3L, "Dan", LocalDate.of(2019, 3, 21), 300L),
new Order(4L, "Sam", LocalDate.of(2019, 4, 21), 400L),
new Order(5L, "Jenny", LocalDate.of(2019, 4, 21), 220L),
new Order(6L, "Jenny", LocalDate.of(2019, 4, 12), 330L)
);
solution1(orders);
}
private static void solution1(List<Order> orders) {
final Map<Month, Map<String, Long>> buyersSummed = new HashMap<>();
for (Order order : orders) {
Map<String, Long> customerAmountMap = buyersSummed.computeIfAbsent(order.getOrderMonth(), mapping -> new HashMap<>());
customerAmountMap.putIfAbsent(order.getCustomer(), 0L);
Long customerAmount = customerAmountMap.get(order.getCustomer());
customerAmountMap.put(order.getCustomer(), customerAmount + order.getAmount());
}
final Map<Month, BuyerDetails> topBuyers = buyersSummed.entrySet().stream()
.collect(
toMap(Entry::getKey, customerAmountEntry -> customerAmountEntry.getValue().entrySet().stream()
.map(entry -> new BuyerDetails(entry.getKey(), entry.getValue()))
.max(Comparator.comparingLong(BuyerDetails::getAmount)).orElseThrow())
);
System.out.println(topBuyers);
}
}
The data model I used:
class BuyerDetails {
String customer;
Long amount;
public BuyerDetails(String customer, Long amount) {
this.customer = customer;
this.amount = amount;
}
public String getCustomer() {
return customer;
}
public Long getAmount() {
return amount;
}
}
class Order {
Long id;
String customer;
LocalDate orderDate;
Long amount;
public Order(Long id, String customer, LocalDate orderDate, Long amount) {
this.id = id;
this.customer = customer;
this.orderDate = orderDate;
this.amount = amount;
}
public Long getId() {
return id;
}
public String getCustomer() {
return customer;
}
public LocalDate getOrderDate() {
return orderDate;
}
public Month getOrderMonth() {
return getOrderDate().getMonth();
}
public Long getAmount() {
return amount;
}
}
The question:
Is there any way to solve the task above in one stream?
Try using groupingBy
, summingLong
and comparingLong
like as shown below
Map<Month, BuyerDetails> topBuyers = orders.stream()
.collect(Collectors.groupingBy(Order::getOrderMonth,
Collectors.groupingBy(Order::getCustomer,
Collectors.summingLong(Order::getAmount))))
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
order -> order.getValue().entrySet().stream()
.max(Comparator.comparingLong(Map.Entry::getValue))
.map(cust -> new BuyerDetails(cust.getKey(), cust.getValue())).get()));
Output
{
"MARCH": { "customer": "Dan", "amount": 300 },
"APRIL": { "customer": "Jenny", "amount": 550 }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With