Reputation: 86935
How can a sum be calculated using the Stream API with nested foreach loops where each has a filter condition?
//java7
Double sum = null;
for (FirstNode first : response.getFirstNodes()) {
if (first.isValid()) {
for (SndNnode snd : first.getSndNodes()) {
if (snd.getType() == NodeType.AMOUNT) {
sum += snd.getAmount();
break;
}
}
}
}
//java8
response.getFirstNodes().stream().filter(first -> first.isValid()).mapToDouble(???).sum();
My snd
foreach loop would be:
first.getSndNodes().stream().filter(snd -> snd.getType() == NodeType.AMOUNT).mapToDouble(snd -> snd.getAmount()).findFirst().sum();
How could I now integrate the snd foreach loop into the first, to get a global sum of the nested lists?
Upvotes: 5
Views: 3856
Reputation: 298579
Your attempt is close to the right solution
response.getFirstNodes().stream()
.filter(FirstNode::isValid)
.mapToDouble(first ->
first.getSndNodes().stream()
.filter(snd -> snd.getType() == NodeType.AMOUNT)
.mapToDouble(snd -> snd.getAmount())
.findAny().orElse(0))
.sum();
If you are sure that there is at most one match in the inner stream, you can use findAny
as there is no requirement on the ordering then. I used the simplest solution for dealing with the possible absence of a match by replacing it with 0
which is transparent to the sum
and saves us from additional filtering.
Upvotes: 4
Reputation: 93902
You could use flatMap
:
response.getFirstNodes()
.stream()
.filter(first -> first.isValid())
.flatMap(first -> first.getSndNodes().stream())
.filter(snd -> snd.getType() == NodeType.AMOUNT)
.mapToDouble(snd -> snd.getAmount())
.sum();
I'm not sure whether that break;
is intentional in your original code.
With the break;
statement, it should looks like this:
response.getFirstNodes()
.stream()
.filter(first -> first.isValid())
.map(first -> first.getSndNodes().stream().filter(snd -> snd.getType() == NodeType.AMOUNT).findFirst())
.filter(Optional::isPresent)
.mapToDouble(opt -> opt.get().getAmount())
.sum();
Basically, for each FirstNode
you test whether it's valid, then you map each FirstNode
to a stream of its SndNode
s for which you find the first that has the type NodeType.AMOUNT
. You need then to filter to get only Optionals that are not empty and for them you get the SndNode
they contains for which you get the corresponding amount.
Upvotes: 8
Reputation: 62884
You can use .flatMap()
the nested nodes. For example:
response.getFirstNodes().stream()
.filter(FirstNode::isValid)
.flatMap(first -> first.getSndNodes().stream())
.filter(snd -> snd.getType == NodeType.AMOUNT)
.mapToDouble(SndNode::getAmount)
.sum();
Upvotes: 2
Reputation: 394156
You can use flatMap to create a single Stream of all the inner lists :
response.getFirstNodes()
.stream()
.filter (first -> first.isValid())
.flatMap (first -> first.getSndNodes().stream())
.filter(snd -> snd.getType() == NodeType.AMOUNT)
.mapToDouble(snd -> snd.getAmount())
.sum();
Upvotes: 2