0

I can't understand why my code doesn't work. Tell me, if it's not difficult, I really ask. error in the stream, but I can't figure out why I can't fill Map from List I want to know what is the student's overall GPA. the result should be in the form: key - group value - student name - score, student name - score

class Student4 {

    String name;
    Subject4[] subjects;

    public Student4(String name, Subject4[] subjects) {
        this.name = name;
        this.subjects = subjects;
    }

    public static void main(String[] args) {

        Student4 s1 = new Student4("Name1", new Subject4[]{
                new Subject4("A", new Integer[]{4, 4, 4, 4, 5}), //21 -> 4.2
                new Subject4("B", new Integer[]{5, 5, 5, 5, 5}), //25 -> 5.0
                new Subject4("C", new Integer[]{4, 4, 4, 4, 5})  //21 -> 4.2
        });
        Student4 s2 = new Student4("Name2", new Subject4[]{
                new Subject4("A", new Integer[]{3, 3, 3, 4, 5}), //18 -> 3.6
                new Subject4("B", new Integer[]{5, 3, 3, 5, 5}), //21 -> 4.2
                new Subject4("C", new Integer[]{4, 3, 3, 4, 3})  //17 -> 3.4
        });
        Student4 s3 = new Student4("Name3", new Subject4[]{
                new Subject4("A", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8
                new Subject4("B", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8
                new Subject4("C", new Integer[]{5, 5, 5, 4, 5})  //24 -> 4.8
        });
        Student4 s4 = new Student4("Name4", new Subject4[]{
                new Subject4("A", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8
                new Subject4("B", new Integer[]{5, 5, 5, 4, 5}), //24 -> 4.8
                new Subject4("C", new Integer[]{5, 5, 5, 4, 5})  //24 -> 4.8
        });

        Map<String, List<Student4>> group = new HashMap<>();
        group.put("group1", List.of(s1, s2));
        group.put("group2", List.of(s3, s4));



        Map<String, Map<String, Double>> averageByStudent = group.entrySet().stream()
                .collect(Collectors.toMap(entry -> entry.getKey(),
                        entry -> entry.getValue().stream()
                                .collect(Collectors.toMap(entry.getValue().stream()
                                                .map(z -> z.name),
                                        entry.getValue().stream()
                                                .flatMap(v -> Arrays.stream(v.subjects))
                                                .flatMap(w -> Arrays.stream(w.mark))
                                                .mapToDouble(Integer::doubleValue)
                                                .average().getAsDouble()
                                ))));
    }
}

class Subject4 {

    String name;
    Integer[] mark;

    public Subject4(String name, Integer[] mark) {
        this.name = name;
        this.mark = mark;
    }
}
R Zav
  • 29
  • 4

3 Answers3

0

I think the issue is in using the Collectors.toMap, this function is expecting two functions as a parameter; one to produce the key and another to produce the value (It doesn't expect a stream of values)

This should work

import static java.util.stream.Collectors.toMap;

Map<String, Map<String, Double>> averageByStudent = group.entrySet().stream()
        .collect(toMap(Entry::getKey,
            entry -> entry.getValue().stream()
                .collect(toMap(student -> student.name, student ->
                    Arrays.stream(student.subjects)
                        .flatMap(w -> Arrays.stream(w.mark))
                        .mapToDouble(Integer::doubleValue)
                        .average().orElse(0d)
                ))));
Islam Elbanna
  • 1,438
  • 2
  • 9
  • 15
0

For figuring this out I would follow the following approach:

Use a proper context relative name for your variable names in your lambda, so you can figure out entry have different meanings in your first and second collect calls (I renamed them to entryKeyStringValueListOfStudents and student to have an idea of what's in there on each situation).

With renaming you'll probably find out the line

Collectors.toMap(entry.getValue().stream()
.map(z -> z.name),

is meaningless as your currently "streamed" object is a Student so there's no need for mapping.

Then changing both you get simpler code: student -> student.studentName, for rendering the key and student -> Arrays.stream(student.subjects) for mapping the subjects (there's no need for a flatMap there, you just stream the subjects and map the marks).

After those changes (and some indentation for clarification, not the most beautiful though) you'll finally get:

Map<String, Map<String, Double>> averageByStudent = group.entrySet().stream()
        .collect(Collectors.toMap(
                entryKeyStringValueListOfStudents -> entryKeyStringValueListOfStudents.getKey(),
                entryKeyStringValueListOfStudents -> entryKeyStringValueListOfStudents.getValue()
                        .stream() //stream of students
                        .collect(Collectors.toMap(
                                    student -> student.studentName,
                                    student -> Arrays.stream(student.subjects) //stream of subjects
                                        .flatMap(subject -> Arrays.stream(subject.mark)) //stream of marks
                                        .mapToDouble(Integer::doubleValue)
                                        .average()
                                        .getAsDouble()
                                                 )
                                )
                        )
                );
Ale Zalazar
  • 1,875
  • 1
  • 11
  • 14
0

Correction:

Map<String, Map<String, Double>> averageByStudent = group.entrySet().stream()
        .collect(Collectors.toMap(grp -> grp.getKey(), // group1
                grp -> grp.getValue().stream() // s1, s2 etc..
                        .collect(Collectors.toMap(student -> student.name,
                                student -> Arrays.stream(student.subjects)
                                .flatMap(subject -> Arrays.stream(subject.mark))
                                .mapToDouble(Double::valueOf).average().getAsDouble()))
        ));