Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Streams: Organize a collection into a map and select smallest key

I'm pretty sure this is not possible in one line, but I just wanted to check:

List<WidgetItem> selectedItems = null;
Map<Integer, List<WidgetItem>> itemsByStockAvailable = WidgetItems.stream()
     .collect(Collectors.groupingBy(WidgetItem::getAvailableStock));
selectedItems = itemsByStockAvailable.get(
     itemsByStockAvailable.keySet().stream().sorted().findFirst().get());

Basically I'm collecting all widget items into a map where the key is the availableStock quantity and the value is a list of all widgets that have that quantity (since multiple widgets might have the same value). Once I have that map, I would want to select the map's value that corresponds to the smallest key. The intermediate step of creating a Map isn't necessary, it's just the only way I could think of to do this.

like image 972
IcedDante Avatar asked Mar 19 '16 23:03

IcedDante


2 Answers

It appears what you want is to keep all the widget items that were grouped with the lowest available stock. In that case, you can collect the grouped data into a TreeMap to ensure the ordering based on increasing values of the stock and retrieve the first entry with firstEntry()

List<WidgetItem> selectedItems = 
    widgetItems.stream()
               .collect(Collectors.groupingBy(
                  WidgetItem::getAvailableStock,
                  TreeMap::new,
                  Collectors.toList()
               ))
               .firstEntry()
               .getValue();

The advantage is that it is done is one-pass over the initial list.

like image 134
Tunaki Avatar answered Oct 23 '22 06:10

Tunaki


Essentially you want to get all the input elements which are minimal according to the custom comparator Comparator.comparingInt(WidgetItem::getAvailableStock). In general this problem could be solved without necessity to store everything into the intermediate map creating unnecessary garbage. Also it could be solved in single pass. Some interesting solutions already present in this question. For example, you may use the collector implemented by Stuart Marks:

List<WidgetItem> selectedItems = widgetItems.stream()
           .collect(maxList(
               Comparator.comparingInt(WidgetItem::getAvailableStock).reversed()));

Such collectors are readily available in my StreamEx library. The best suitable in your case is MoreCollectors.minAll(Comparator):

List<WidgetItem> selectedItems = widgetItems.stream()
           .collect(MoreCollectors.minAll(
               Comparator.comparingInt(WidgetItem::getAvailableStock)));
like image 2
Tagir Valeev Avatar answered Oct 23 '22 08:10

Tagir Valeev