Reputation: 143
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
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
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
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