Reputation:
I have a class with a collection of Seed
elements. One of the method's return type of Seed
is Optional<Pair<Boolean, Boolean>>
.
I'm trying to loop over all seeds
, keeping the return type (Optional<Pair<Boolean, Boolean>>
), but I would like to be able to say if there was at least true
value (in any of the Pair
s) and override the result with it. Basically, if the collection is (skipping the Optional
wrapper to make things simpler): [Pair<false, false>
, Pair<false, true>
, Pair<false, false>
] I would like to return and Optional
of Pair<false, true>
because the second element had true
. In the end, I'm interested if there was a true
value and that's about it.
public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
return seeds.stream()
.map(Seed::hadExposure)
...
}
I was playing with reduce
but couldn't come up with anything useful.
My question is related with Java streams directly. I can easily do this with a
for
loop, but I aimed initially for streams.
Upvotes: 0
Views: 1357
Reputation: 51060
Since you're Java 11, you can use Optional::stream
(introduced in Java 9) to get rid of the Optional
wrapper. As a terminal operation, reduce
is your friend:
public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
// wherever the seeds come from
Stream<Optional<Pair<Boolean, Boolean>>> seeds = seeds();
return seeds
.flatMap(Optional::stream)
.reduce((pair1, pair2) -> new Pair<>(
pair1.left() || pair2.left(),
pair1.right() || pair2.right())
);
}
If you want to go a step further and give your Pair
a general way to be folded with another Pair
into a new instance, you can make the code a bit more expressive:
public class Pair<LEFT, RIGHT> {
private final LEFT left;
private final RIGHT right;
// constructor, equals, hashCode, toString, ...
public Pair<LEFT, RIGHT> fold(
Pair<LEFT, RIGHT> other,
BinaryOperator<LEFT> combineLeft,
BinaryOperator<RIGHT> combineRight) {
return new Pair<>(
combineLeft.apply(left, other.left),
combineRight.apply(right, other.right));
}
}
// now you can use fold and Boolean::logicalOr
// https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Boolean.html#logicalOr(boolean,boolean)
public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
Stream<Optional<Pair<Boolean, Boolean>>> seeds = seeds();
return seeds
.flatMap(Optional::stream)
.reduce((pair1, pair2) -> pair1
.fold(pair2, Boolean::logicalOr, Boolean::logicalOr))
);
}
I probably wouldn't create Pair::fold
just for this use case, but I would be tempted. ;)
Upvotes: 2
Reputation: 556
using Collectors.partitioningBy
you can get a Map with boolean keys after that you can easily retrieve values indexed with the key true
Optional<Pair<Boolean, Boolean>> collect = Arrays.asList(pair1, pair2, par3).stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.collectingAndThen(Collectors.partitioningBy(p -> p.getFirst() == true || p.getSecond() == true),
m -> m.get(true).stream().findAny()));
Upvotes: 0
Reputation: 34460
As you've tagged this question with java-11
, you can make use of the Optional.stream
method:
public Optional<Pair<Boolean, Boolean>> hadAnyExposure() {
return Optional.of(
seeds.stream()
.flatMap(seed -> seed.hadExposure().stream())
.collect(
() -> new Pair<Boolean, Boolean>(false, false),
(p, seed) -> {
p.setLeft(p.getLeft() || seed.getLeft());
p.setRight(p.getRight() || seed.getRight());
},
(p1, p2) -> {
p1.setLeft(p1.getLeft() || p2.getLeft());
p1.setRight(p1.getRight() || p2.getRight());
}));
}
This first gets rid of the Optional
by means of the Optional.stream
method (keeping just the pairs) and then uses Stream.collect
to mutably reduce the pairs by means of the OR associative operation.
Note: using Stream.reduce
would also work, but it would create a lot of unnecessary intermediate pairs. That's why I've used Stream.collect
instead.
Upvotes: 0
Reputation: 607
Your thoughts on reduce
look like the right way to go, using ||
to reduce both sides of each Pair
together. (Not exactly sure what your Optional
semantics are, so going to filter out empty ones here and that might get what you want, but you may need to adjust):
Optional<Pair<Boolean, Boolean>> result = seeds.stream().map(Seed::hadExposure)
.filter(Optional::isPresent)
.map(Optional::get)
.reduce((a, b) -> new Pair<>(a.first || b.first, a.second || b.second));
Upvotes: 0