pantuptus :
I have the following class:
public class Offer {
private final OfferType type;
private final BigDecimal price;
// constructor, getters and setters
}
and enum type:
public enum OfferType {
STANDARD, BONUS;
}
My use case is that having a list of offers as an input, I want to filter out all the standard ones except the cheapest one. So for the following input data
List<Offer> offers = Arrays.asList(new Offer(OfferType.STANDARD, BigDecimal.valueOf(10.0)),
new Offer(OfferType.STANDARD, BigDecimal.valueOf(20.0)),
new Offer(OfferType.STANDARD, BigDecimal.valueOf(30.0)),
new Offer(OfferType.BONUS, BigDecimal.valueOf(5.0)),
new Offer(OfferType.BONUS, BigDecimal.valueOf(5.0)));
I expect the following result
[Offer [type=STANDARD, price=10.0], Offer [type=BONUS, price=5.0], Offer [type=BONUS, price=5.0]]
Is there a single-line statement (using streams or any third-party library) that allows for doing that?
Eugene :
Not with a single stream operation though:
List<Offer> some = offers.stream()
.filter(x -> x.getType() != OfferType.STANDARD)
.collect(Collectors.toCollection(ArrayList::new));
offers.stream()
.filter(x -> x.getType() == OfferType.STANDARD)
.min(Comparator.comparing(Offer::getPrice))
.ifPresent(some::add);
If you find yourself doing this a lot, may be spin a custom collector:
public static Collector<Offer, ?, List<Offer>> minCollector() {
class Acc {
Offer min = null;
List<Offer> result = new ArrayList<>();
void add(Offer offer) {
if (offer.getType() == OfferType.STANDARD) {
if (min == null) {
min = offer;
} else {
min = offer.getPrice()
.compareTo(min.getPrice()) > 0 ? min : offer;
}
} else {
result.add(offer);
}
}
Acc combine(Acc another) {
this.min = reduceMin(this.min, another.min);
result.addAll(another.result);
return this;
}
List<Offer> finisher() {
result.add(min);
return result;
}
private Offer reduceMin(Offer left, Offer right) {
return Collections.min(Arrays.asList(left, right),
Comparator.nullsLast(Comparator.comparing(Offer::getPrice)));
}
}
return Collector.of(Acc::new, Acc::add, Acc::combine, Acc::finisher);
}
And usage would be:
List<Offer> result = offers.stream()
.collect(minCollector());