diego
diego

Reputation: 150

Calculate the sum of values in a Map from Stream Api

I'm a completely newbie, so I ask you not to swear too much)

There is a collection (ArrayList) that keeps objects of the class Student (<HashMap <Subject, Integer> rating, String name>). I must calculate the average score for a particular subject using the Stream API. I Tried to do it like this:

 sumOnSubjectInGroup = studentsInGroup.stream()
        .filter(e -> e.getRating().entrySet().iterator().next().getKey().equals(subject))
        .mapToInt(e -> e.getRating().entrySet().iterator().next().getValue())
        .reduce(0, (x, y) -> x + y);
 countMarkInGroup = (int) studentsInGroup.stream()
        .filter(e -> e.getRating().entrySet().iterator().next().getKey().equals(subject))
        .count();
 return (double) sumOnSubjectInGroup / countMarkInGroup;

where sumOnSubjectInGroup is the sum of all students' subject scores. countMarkInGroup - the number of such marks. studentsInGroup is a collection with all Student objects. getRating () - returns a HashMap <Subject, Integer> with a list of items and ratings for it. But obviously I got the wrong result

Upvotes: 0

Views: 816

Answers (2)

Nowhere Man
Nowhere Man

Reputation: 19545

You may use Collectors.averagingInt to calculate the average value without separate calculation of sum and count by subject.

import java.util.*;
import java.util.stream.*;

class Student {
    String name;
    Map<String, Integer> rating;
    
    public Student(String name, Map<String, Integer> grades) {
        this.name = name;
        this.rating = grades;
    }
    
    public String getName() { return name; }
    public Map<String, Integer> getRating() { return rating; }
}

public class Main {
    public static void main(String args[]) {
        List<Student> students = Arrays.asList(
            new Student("John", Map.of("Math", 90, "Physics", 94, "Chemistry", 93, "English", 92)),
            new Student("Jack", Map.of("Math", 73, "Physics", 76, "English", 89)),
            new Student("Jane", Map.of("Math", 87, "Chemistry", 91, "French", 94)),
            new Student("Joel", Map.of("Math", 81, "Physics", 79, "French", 88))
        );
        students
            .stream()
            .flatMap(s -> s.getRating().entrySet().stream())
            .collect(Collectors.groupingBy(
                Map.Entry::getKey, Collectors.averagingInt(Map.Entry::getValue)))
            .entrySet()
            .forEach(System.out::println);
    }
}

Output:

English=90.5
Chemistry=92.0
French=91.0
Math=82.75
Physics=83.0

Upvotes: 2

Eklavya
Eklavya

Reputation: 18430

You can use .average() to calculate average and use containsKey to check existence in the map to filter and get the score.

Double average = studentsInGroup.stream()
            .filter(e -> e.getRating().containsKey(subject))
            .mapToInt(e -> e.getRating().get(subject))
            .average()
            .orElse(0.0);

Upvotes: 5

Related Questions