Karl Tryggvason
Karl Tryggvason

Reputation: 143

Find min value by attribute, return as list

I have a class similar to this:

public class Product{
    public Integer category;
    public Integer price;    

    // getters and setters omitted
}

With a list of products with different categories and prices I want to return a list containing the cheapest product for each category. I have found a way to get this as a map with the category as a key, but I'd like to just have a list. What is a concise and readable way to do that?

This is what I have so far that gives me a map:

Map<String, Product> cheapestProducts = products.stream()
   .collect(
       groupingBy(                                                      
           Product::getCategory,
               collectingAndThen(minBy(comparing(Product::getPrice)), p -> p.get())
       )
   );

Upvotes: 3

Views: 220

Answers (3)

user140547
user140547

Reputation: 8200

In my opinion, your method is OK. The problem is with readability, and the solution is in my opinion to extract methods to make it reuseable, if needed. Java 8 streams are fluent and so on, but there is often the temptation to make streams which are 20 lines long, which harms readability. So by extracting the method getMinBy() (maybe there is a better name for it), which encapsulates the minBy, and Optional::get, it gets more readable in my opinion. Of course, the method itself is not that concise, but is reusable.

    Map<Integer, Product> cheapestProducts = products.stream()
            .collect(
                    groupingBy(
                            Product::getCategory,
                            getMinBy(Product::getPrice)
                    ));


    private static <T,C extends Comparable<? super C>> Collector<T, ?, T> getMinBy(Function<T, C> comparatorFunction) {
        return collectingAndThen(minBy(comparing(comparatorFunction)), Optional::get);
    }

Upvotes: 1

Mrinal
Mrinal

Reputation: 1906

You can use toMap method and then call map.values() :

Collection<Product> cheapestProducts = products
            .stream()
            .collect(Collectors.toMap(t -> t.category, u -> u,
                    (a, b) -> a.price > b.price ? b : a))
            .values();

Also see Holger's comment above, if you really want a list.

Upvotes: 2

Jacques Ramsden
Jacques Ramsden

Reputation: 871

To get values from a Map to a list you can do something like this...

List<Value> list = new ArrayList<Value>(map.values());

Upvotes: 0

Related Questions