Home > Back-end >  Fetch max value in a list of objects which are inside list of objects by month using Java Stream
Fetch max value in a list of objects which are inside list of objects by month using Java Stream

Time:08-12

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 of String.
  • YearMonth from java.time package instead of two String 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)
));
  • Related