Reputation:
I have an ArrayList
with Bottle
objects. Each Bottle object has a brand:String
, and a price:double
field. I am trying to write an expression using Stream API, so not only I end up with a list of unique bottles, but with a list of unique bottles with the highest price - e.g. if the ArrayList
contains three Bottle objects of the same name, I would like the one with the highest price to make it into the final list.
Upvotes: 0
Views: 687
Reputation: 1
If it contains more than two properties Collectors.toMap will not work. Better solution would be
List<Bottle> processedBottles = bottles.stream()
.sorted(Comparator.comparing(Bottle::getPrice).reversed())
.filter(distinctByKey(Bottle::getName))
.collect(Collectors.toList());
//print
processedBottles.forEach(System.out::println);
Distinct by key:
public static <T> Predicate<T> distinctByKey(
Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
Upvotes: 0
Reputation: 950
Well here is an example of how you can find unique with highest prices
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.stream.Collectors;
public class ArrayTest {
public static class Bottle {
private final String name;
private final double price;
public Bottle(final String name, final double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return "Bottle{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
@Test
public void uniqueBottles() {
List<Bottle> bottles = List.of(
new Bottle("bottle1", 1.),
new Bottle("bottle1", 2.),
new Bottle("bottle1", 3.),
new Bottle("bottle2", 3.5),
new Bottle("bottle2", 1.5)
);
var processedBottleList = bottles
.stream()
.collect(Collectors.toMap(Bottle::getName, Bottle::getPrice, (p, p2) -> p > p2 ? p : p2))
.entrySet()
.stream()
.map(e -> new Bottle(e.getKey(), e.getValue()))
.collect(Collectors.toList());
System.out.println(processedBottleList);
}
}
Upvotes: 1
Reputation: 21124
You can create a map, with brand name as the key and bottle as the value. And then use a merge function in which, if another bottle is found with the same name, it keeps the bottle with the highest price. Then, get hold of the map values at the end. Here's hot it looks in practice.
Collection<Bottle> highestPriceBottles = bottles.stream()
.collect(Collectors.toMap(Bottle::getBrand, b -> b,
BinaryOperator.maxBy(Comparator.comparingDouble(Bottle::getPrice))))
.values();
Upvotes: 3