Reputation: 2366
I'm dealing with the following POJO:
@Getter
@AllArgsConstructor
public class Order {
private int quantity;
private Category category;
private LocalDate date;
//rest fields
}
I have a list of this objects and what I'm trying to do is I want to find month (extracted from date
field) and category (which is enum type) which has the highest value of quantity
. I wanna do it with streams, I made few attempts, but now I'm out of ideas. Could you help me with this?
Below my dummy attempt to resolve this case:
Optional<Entry<Month, Order>> result = orders.stream()
.collect(
Collectors.groupingBy(Order::getLocalDate, Collectors.maxBy(Comparator.comparingInt(Order::getQuantity))))
.entrySet()
.stream()
.collect(Collectors.toMap(e -> e.getKey().getMonth(), e -> e.getValue().get()))
.entrySet()
.stream()
.max(Entry.comparingByValue(Comparator.comparingInt(Order::getQuantity)));
Example, we have the following list of data:
List<Order> orders = Arrays.asList(
new Order(Category.HEADPHONES,3, LocalDate.of(2018, 2, 22)),
new Order(Category.HEADPHONES,6, LocalDate.of(2018, 2, 23)),
new Order(Category.NOTEBOOKS,8, LocalDate.of(2018, 2, 24)),
new Order(Category.NOTEBOOKS,4, LocalDate.of(2018, 3, 3)),
new Order(Category.NOTEBOOKS,1, LocalDate.of(2018, 3, 3)),
new Order(Category.PHONES,2, LocalDate.of(2018, 3,5)),
new Order(Category.PHONES,2, LocalDate.of(2018, 3,7))
);
I want to play with streams and receive the result (e.g some Tuple which contains month
, category
, quantity
).
In the above data-set month with the highest quantity value (9
) was February
in HEADPHONES
category.
Upvotes: 0
Views: 549
Reputation: 26036
Here is an example of how to do this with respect to month only (another class can be introduced to compose keys for aggregation):
// month, sum
Map<Integer, Integer> map = orders.stream()
.collect(groupingBy(o -> o.getDate().getMonthValue(), TreeMap::new, summingInt(Order::getQuantity)));
int month = map.entrySet().stream().reduce((s1, s2) -> {
if (s1.getValue() > s2.getValue()) return s1;
else return s2;
}).get().getKey();
I have separated it into two streams for clarity, but you can join them. First one gives month number with sum of orders, second the greatest month value.
Upvotes: 1
Reputation: 45309
You can group by a pair (using SimpleEntry
in this example) of the two fields, compute the sum of quantity per group, then stream over the result to pick the entry with the highest value:
orders.stream()
.collect(Collectors.groupingBy(
o -> new SimpleEntry<>(o.getDate().getMonth(), o.getCategory()),
Collectors.summingInt(Order::getQuantity)))
.entrySet()
.stream()
.max(Entry.comparingByValue())
.ifPresent(cat -> System.out.println(
"Month: " + cat.getKey().getKey() +
" - Category: " + cat.getKey().getValue() +
" - Quantity: " + cat.getValue()));
The output of this with your example data is:
Month: FEBRUARY - Category: HEADPHONES - Quantity: 9
Upvotes: 2