Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
591 views
in Technique[技术] by (71.8m points)

java - 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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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()))

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...