Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sum attribute of object with Stream API

I currently have the following situation:

I have got a Report object which can contain multiple Query objects. The Query objects have properties: Optional<Filter> comparisonFilter, Optional<String> filterChoice and int queryOutput.

Not every query has a comparison filter, so I first check on that. Then, I make sure I get the queries for a particular filter (which is not the problem here, so I will not discuss this in detail). Every filter has some choices, of which the number of choices is variable.

Here is an example of the input (these Query objects all have the same comparisonFilter):

Query 1 -- Choice: 'First' -- Output: 10
Query 1 -- Choice: 'First' -- Output: 5
Query 1 -- Choice: 'Second' -- Output: 25
Query 1 -- Choice: 'Third' -- Output: 10

Now, I would like to sum the query outputs for every unique choice. I currently have this code:

report
.getQueries()
.stream()
.filter(q -> q.getComparisonFilter().isPresent())
.filter(q -> q.getComparisonFilter().get().equals(view.getFilter().get()))
.forEach(query -> {

    //Sum the query outputs per choice

});

I could do this by creating a Map<String, Integer>, where the key is the choice and the value is the query input. But then I would need to loop through the Map again to use the value for something (which is not important here).

The output should be like this:

Choice: 'First' -- Summed Output: 15
Choice: 'Second' -- Summed Output: 25
Choice: 'Third' -- Summed Output: 10

But I would like to use this 'Summed Output' directly in a forEach on the stream, but if this is not possible or practical anymore, I am okay with that.

I would like to do this the 'Java 8'-way, but I can not seem to find out how.

So my question is: Is it possible to do this shorter with the new Stream API?

Note: If anyone has some ideas about how I could make this question more general (maybe a better title and some generalizations), please let me know!

like image 859
bashoogzaad Avatar asked Feb 18 '15 14:02

bashoogzaad


People also ask

How do I sum in stream API?

sum() The Stream API provides us with the mapToInt() intermediate operation, which converts our stream to an IntStream object. This method takes a mapper as a parameter, which it uses to do the conversion, then we can call the sum() method to calculate the sum of the stream's elements.

How do you sum values in Java?

So you simply make this: sum=sum+num; for the cycle. For example sum is 0, then you add 5 and it becomes sum=0+5 , then you add 6 and it becomes sum = 5 + 6 and so on.


1 Answers

If I understand well, you are indeed looking for a groupingBy and then you have to group the values by summing their int property.

The groupingBy will give you a Map<String, List<Query>> but then the downstream collector (Collectors.summingInt in this case) will sum all the int values of the Query instances in the list, resulting in a Map<String, Integer>.

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summingInt;
...

Map<String, Integer> map = 
    report.getQueries()
          .stream()
          .filter(q -> q.getComparisonFilter().isPresent())
          .filter(q -> q.getComparisonFilter().get().equals(view.getFilter().get()))
          .collect(groupingBy(q -> q.filterChoice.get(), summingInt(q -> q.queryOutput)));

Note that you should check whether the filterChoice Optional is not empty (maybe add another filter clause?). You can see this small gist for a basic and simplified demo to illustrate the principle.

Also the Optional class provide a sensible implementation of equals so the filter clause could looks like this:

.filter(q -> q.getComparisonFilter().equals(view.getFilter()))
like image 164
Alexis C. Avatar answered Nov 14 '22 12:11

Alexis C.