I have list of Store objects and each Store object has a list of Sales objects. I want to fetch the Store object which has the highest sale for any item for a particular month.
public class Store {
private String storeName;
private String contactInfo;
private List<Sales> sales;
//getter & setter
}
public class Sales {
private String month;
private String year;
private BigInteger price;
//getter & setter
}
As of now I'm able to filter the list of Store objects by month
List<Store> stores = list.stream()
.filter(s -> s.getPrice().stream().anyMatch(t -> t.getMonth().contains("Jan")))
.collect(Collections.toList());
But I want to filter this list further to get a single store object which has the max price for the month of January.
EDIT : sample list structure in JSON format
[
{
"storeName": "abc",
"contactInfo": "xcb",
"sales": [{
"month" : "Jan",
"year": "2022",
"price": 3000
},
{
"month" : "Feb",
"year": "2022",
"price": 3300
}
]
},
{
"storeName": "abcde",
"contactInfo": "xcbe",
"sales": [{
"month" : "Jan",
"year": "2022",
"price": 2000
},
{
"month" : "Feb",
"year": "2022",
"price": 4000
}
]
}
]
Thank you!
CodePudding user response:
Firstly, I would advise to use appropriate data types:
BigDecimal
for price instead ofString
.YearMonth
fromjava.time
package instead of twoString
fields.
public class Sales {
private YearMonth yearMonth;
private BigDecimal price;
// getters
}
The method responsible for finding the Store
having the highest price for a particular YearMonth
might be implemented like that:
public static Store getMostExpensiveStoreByMonth(List<Store> stores,
YearMonth yearMonth) {
return stores.stream()
.collect(Collectors.groupingBy( // intermediate map Map<Store, Optional<BigDecimal>>
Function.identity(),
Collectors.flatMapping(store -> store.getSales().stream()
.filter(sales -> sales.getYearMonth().equals(yearMonth))
.map(Sales::getPrice),
Collectors.maxBy(Comparator.naturalOrder()))
))
.entrySet().stream()
.filter(entry -> entry.getValue().isPresent()) // Optional would be empty if there was no sales during the given month in a particular store
.max(Comparator.comparing((Map.Entry<Store, Optional<BigDecimal>> entry) -> entry.getValue().get()))
.map(Map.Entry::getKey)
.orElseThrow();
}
CodePudding user response:
You can flat map to pairs of (store, price) and select the max:
stores.stream()
.flatMap(store -> store.getSales()
.stream()
.filter(sales -> sales.getMonth().contains("Jan"))
.map(sales -> Map.entry(store, sales.getPrice())))
.max(Entry.comparingByValue())
.map(Entry::getKey)
CodePudding user response:
If your data source is a JSON string, it can be done by a JSON query language such as Josson directly.
https://github.com/octomix/josson
Josson josson = Josson.fromJsonString(
"["
" {"
" \"storeName\": \"abc\","
" \"contactInfo\": \"xcb\","
" \"sales\": [{"
" \"month\" : \"Jan\","
" \"year\": \"2022\","
" \"price\": 3000"
" },"
" {"
" \"month\" : \"Feb\","
" \"year\": \"2022\","
" \"price\": 3300"
" }"
" ]"
" },"
" {"
" \"storeName\": \"abcde\","
" \"contactInfo\": \"xcbe\","
" \"sales\": [{"
" \"month\" : \"Jan\","
" \"year\": \"2022\","
" \"price\": 2000"
" },"
" {"
" \"month\" : \"Feb\","
" \"year\": \"2022\","
" \"price\": 4000"
" }"
" ]"
" }"
"] ");
JsonNode node = josson.getNode("findByMax(sales[month='Jan'].price)");
System.out.println(node.toPrettyString());
Output
{
"storeName" : "abc",
"contactInfo" : "xcb",
"sales" : [ {
"month" : "Jan",
"year" : "2022",
"price" : 3000
}, {
"month" : "Feb",
"year" : "2022",
"price" : 3300
} ]
}
CodePudding user response:
Why are people making this so complicated?
We only need to find a good comparator, then we don't need intermediate flatmap operations:
return stores.stream().max(Comparator.comparing(
store -> store.getSales().stream()
.findFirst(sales -> sales.getMonth().contains("Jan"))
.map(sales -> sales.getPrice())
.orElse(0)
));