Reputation: 21
I have a data Map<String, List<Students>>
where the key is the subject name.
I want to iterate through each subject and add the score of other subjects corresponding to a student.
I tried the follo
wing:
data.entrySet().stream().forEach(subject -> sumScoresOfAStudent(subject, data));
private void sumScoresOfAStudent(Entry<String, List<Student>> subject, Map<String, List<Student>> data) {
String firstSubject = subject.getKey();
List<Student> firstSubjectScores = subject.getValue();
List<Student> otherSubjectScores = data.entrySet().stream()
.filter(subjectData -> !subjectData.getKey().equalsIgnoreCase(firstSubject))
.map(subjectData -> subjectData.getValue()).flatMap(subjectData -> subjectData.stream())
.collect(Collectors.toList());
...
}
But here I noticed it would loop 3 times for the same data if I have 3 subjects as my Key. Is there any other better way to iterate it only once ?
Thanks in advance for your help.
Eg:
Student s11 = new Student("A", 20);
Student s21 = new Student("B", 10);
data.put("Maths", students);
Student s12 = new Student("A", 30);
Student s22 = new Student("B", 40);
data.put("Science", students);
Student s13 = new Student("A", 45);
Student s23 = new Student("B", 20);
data.put("History", students);
Expected Output :
Student A - 95
Student B - 70
Upvotes: 2
Views: 1128
Reputation: 425033
I think your life would be easier if you first created a map of student to their subjects:
Map<Student, List<String>> studentSubjects = new HashMap<>();
data.forEach((k, v) -> v.forEach(studentSubjects.computeIfAbsent(v, x -> new ArrayList<>()).add(k)));
But your “Student” class does not actually represent a student, but rather a student’s result for a subject. You should fix that too.
Upvotes: 0
Reputation: 34460
You have a map of lists aka a multimap and it seems that you want to get the total scores per student. You could use a Map<String, Integer>
for this, with the key being the student name and the value the total score.
Assuming Student
has getName()
and getScore()
methods, here's a way to do what you want without streams:
Map<String, Integer> result = new LinkedHashMap<>();
data.forEach((k, students) ->
students.forEach(s -> result.merge(s.getName(), s.getScore(), Integer::sum)));
This uses Map.forEach
, Iterable.forEach
and Map.merge
. Check the docs to see how they work.
First, we are iterating the map. Then, for each entry (k, students)
of the map, we iterate the values (which is a list of students). For each element student
of the list, we put their name and score in the result
map using the Map.merge
method.
The Map.merge
method works the same as Map.put
, but if there's already an entry with the same key, it merges the present value with the new value using the 3rd argument (in this case Integer::sum
) which, well, sums the old and new values for that key.
Upvotes: 0
Reputation: 40034
Sorry, I entirely misunderstood what you wanted. This presumes the Student class has the getters getScore
and getName
. This simply takes the map values
which are lists of students
and the flattens them into one large stream and computes the sum of each students score.
Map<String, Integer> scores = data.values().stream().flatMap(List::stream)
.collect(Collectors.groupingBy(Student::getName,
Collectors.summingInt(Student::getScore)));
scores.forEach((name,score) -> System.out.printf("Student %s - %s%n",
name,score));
Prints
Student A - 95
Student B - 70
Upvotes: 2