Calculate the sum of values in Map via the Stream Api

There is a collection ArrayList that stores objects of the Student(<HashMap<Subject, Integer> rating, String name>) class. You need to calculate the average score for a specific 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 - the total score for a particular subject (subject) of all students.
  • countMarkInGroup - the number of such estimates.
  • studentsInGroup - collection with all Student objects.
  • getRating() - returns HashMap<Subject, Integer> with a list of subjects and grades for to him.
  • subject - the subject in which I want to learn the average score.
    Student student1 = new Student(new HashMap<>() {{
                    put(BIOLOGY, 8);
                    put(MATH, 4);
                    put(ENGLISH, 10);
                    put(PHYSIC, 6);
                }}, "Ivanov Ivan Ivanovich");
    Student student2 = new Student(new HashMap<>() {{
                    put(MATH, 5);
                    put(ASTRONOMY, 6);
                    put(CHEMISTRY, 2);
                }}, "Petrov Petr Petrovovich");
    
    Student student3 = new Student(new HashMap<>() {{
                    put(ASTRONOMY, 7);
                    put(PHYSIC, 4);
                    put(HISTORY, 5);
                }}, "Vasiliev Vasiliy Vasilievich");
    
    Student student4 = new Student(new HashMap<>() {{
                    put(ASTRONOMY, 4);
                    put(MATH, 10);
                }}, "Sergeev Sergei Sergeevich");      
    
    Group group1 = new Group("TEEO", new ArrayList<>() {{
                    add(student1);
                    add(student2);
                }});
    Group group2 = new Group("THEORETICAL PHYSICS", new ArrayList<>() {{
                    add(student3);
                    add(student4);
                }});
    
    Faculty faculty1 = new Faculty("PHYSICS FACULTY", new ArrayList<>() {{
                    add(group1);
                    add(group2);
                }});
    
    faculty1.averageMarkOnSubjectOnFaculty(MATH);

public void averageMarkOnSubjectOnFaculty(Subject subject) {
        averageMarkOnSomeSubjectOnFaculty = facultyList.stream()
                .map(e -> e.getStudentsInGroup())
                .map(e -> e.iterator().next())
                .filter(e -> e.getRating().containsKey(subject))
                .mapToInt(e -> e.getRating().get(subject))
                .average()
                .orElse(0.0);

        if (averageMarkOnSomeSubjectOnFaculty == 0)
            System.out.println("There are no students on the faculty " + getNameFaculty() +
                    " which learning " + subject + ".");
        else System.out.println("On the faculty " + getNameFaculty() + " in the subject " + subject +
                " average mark " + averageMarkOnSomeSubjectOnFaculty);
    }
Author: robert0801, 2020-08-28

2 answers

Let's say you have a student like this:

class Student{

    Map<String, Integer> rating;
    String name;

    public Student(String name){
        rating = new HashMap<>();
        this.name = name;
    }

    public Student rate(String subject, Integer rate){
        rating.put(subject, rate);
        return this;
    }
}

Then the counting method can be as follows:

private static Double avgRate(String subject, List<Student> students){
    return students
            .stream()
            .mapToInt(student -> student.rating.get(subject))
            .average()
            .orElse(0);
}

UPD: If we use faculty and groups, we can do this:

private static Double avgRate(String subject, Faculty faculty){
    return faculty
            .getGroups()
            .stream()
            .flatMap(group -> group.getStudents().stream())
            .mapToInt(student -> student.rating.get(subject))
            .average()
            .orElse(0);
}

Where we extract a list of groups from the faculty, then use flatmap to open a stream with students from each group. And then as in the first example.

 2
Author: Alexey R., 2020-08-28 21:55:59

When using the e.getRating().entrySet().iterator().next() construct, you will actually get a random item, which will lead to incorrect data.

package com.somepackage;

import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

public class Main {

    @Getter
    @Builder
    @ToString
    @EqualsAndHashCode
    static class Subject {

        private String name;

    }

    @Getter
    @Builder
    @ToString
    @EqualsAndHashCode
    static class Student {

        private Map<Subject, Integer> rating;
        private String name;

    }

    private static List<Subject> prepareSubjects(String...names) {
        return Arrays.stream(names)
                .map(n -> Subject.builder()
                        .name(n)
                        .build())
                .collect(Collectors.toList());
    }

    private static List<Student> prepareStudents(List<Subject> subjects, String...names) {
        return Arrays.stream(names)
                .map(n -> Student.builder()
                        .name(n)
                        .rating(subjects.stream()
                                .collect(
                                        Collectors.toMap(
                                                s -> s,
                                                s -> ThreadLocalRandom.current().nextInt(1, 6))))
                        .build())
                .collect(Collectors.toList());
    }

    private static <T> T randomValue(
            final List<T> values) {
        return values.get(
                ThreadLocalRandom.current().nextInt(
                        values.size()));
    }

    public static void main(String[] args) {
        List<Subject> subjects =
                prepareSubjects(
                        "Java", "C#", "Kotlin", "Scala");
        List<Student> students =
                prepareStudents(
                        subjects,
                        "Ivanova", "Petrov", "Sidorov", "Fedorova");
        Map<Subject, Double> ratings =
                students.stream()
                        .flatMap(s -> s.getRating().entrySet().stream())
                        .collect(Collectors.groupingBy(
                                e -> e.getKey(),
                                HashMap::new,
                                Collectors.averagingDouble(e -> e.getValue())));
        Subject randomSubject =
                randomValue(subjects);
        double randomSubjectRating1 =
                students.stream()
                        .mapToInt(s -> s.getRating().get(randomSubject))
                        .average()
                        .orElse(0.0);
        double randomSubjectRating2 =
                students.stream()
                        .flatMap(s -> s.getRating().entrySet().stream())
                        .filter(e -> randomSubject.equals(e.getKey()))
                        .collect(Collectors.averagingDouble(e -> e.getValue()));
        System.out.println("Students: " + students);
        System.out.println("Ratings: " + ratings);
        System.out.println("Rating by random subject #1: " + randomSubjectRating1);
        System.out.println("Rating by random subject #2: " + randomSubjectRating2);
    }

}
 1
Author: Alexandr, 2020-08-28 19:42:16