I want to do a Map<Person, Double>
, where Double
is average of Integer
values that is stored in another Map <String, Integer>
which is one of the fields of stream's elements.
public Map<Person,Double> totalScores(Stream<CourseResult> programmingResults) {
return
programmingResults.collect(Collectors.groupingBy(
CourseResult::getPerson,
// And there is a problem, I want to get values from `Map <String, Integer>`
// and do the `averagingInt`, but only get
//`Bad return type in lambda expression:
// Collection<Integer> cannot be converted to int`
Collectors.averagingInt(
s -> s.getTaskResults().values()
)
));
}
How can I get these values in the right way?
There's some of the classes I'm using:
public class CourseResult {
private final Person person;
private final Map<String, Integer> taskResults;
public CourseResult(final Person person, final Map<String, Integer> taskResults) {
this.person = person;
this.taskResults = taskResults;
}
public Person getPerson() {
return person;
}
public Map<String, Integer> getTaskResults() {
return taskResults;
}
}
CodePudding user response:
Note that values()
is a Collection<Integer>
. You can't average by that. You can use a flat-mapping Collector
, to flatten each group of persons to just integers, rather than CourseResult
. After that, you can do an average by the identity function.
return programmingResults.collect(
Collectors.groupingBy(
CourseResult::getPerson,
Collectors.flatMapping(s->s.getTaskResults().values().stream(),
Collectors.averagingInt(x -> x)
)
)
);
Edit: If there is only one CourseResult
per Person
, then you don't need groupingBy
. Just use toMap
and calculate the average using another stream.
return programmingResults.collect(
Collectors.toMap(
CourseResult::getPerson,
result -> result.getTaskResults().values()
.stream().mapToInt(x -> x).average().orElse(0))
);
CodePudding user response:
If it is guaranteed that the input stream contains CourseResult
instances which have unique persons (and grouping flatMapping of the task results may not be needed), it may be sufficient to use toMap
collector:
public Map<Person,Double> totalScores(Stream<CourseResult> results) {
return
results.collect(Collectors.toMap(
CourseResult::getPerson,
cr -> cr.getTaskResults().values() // Collection<Integer>
.stream() // Stream<Integer>
.collect(Collectors.averagingInt(Integer::intValue))
)
));
}