javajava-streamaveragecomparator

Java stream API, how to sort by average?


Question:

Create a program that keeps information about some students and their grades.

You will receive an integer number - n.

Then, you will receive 2 * n rows of input.

First, you will receive the student's name. Аfter that, you will receive their grade.

If the student does not exist, add them.

Keep track of all of the grades of each student.

When you finish reading the data, keep only the students which have an average grade higher or equal to 4.50.

Order the filtered students by their average grade in descending order.

Print the students and their average grade in the following format:

"{name} -\> {averageGrade}"

Format the average grade to the second decimal place.

Test input:

5
John
5.5
John
4.5
Alice
6
Alice
3
George
5

Test output:

John -> 5.00
George -> 5.00
Alice -> 4.50

My answer:

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

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = Integer.parseInt(scanner.nextLine());

        Map<String, List<Double>> records = new HashMap<>();

        while(n > 0){
            String name = scanner.nextLine();
            double grade = Double.parseDouble(scanner.nextLine());
            records.putIfAbsent(name, new ArrayList<>());
            records.get(name).add(grade);
            n--;
        }

        records.entrySet().stream().filter(item -> {
            double average = item.getValue().stream().mapToDouble(x -> x).average().getAsDouble();

            return average >= 4.50;
        }).sorted((a, b) -> {
            double average1 = a.getValue().stream().mapToDouble(x -> x).average().getAsDouble();
            double average2 = b.getValue().stream().mapToDouble(x -> x).average().getAsDouble();

            return (int) (average2 - average1);
        }).forEach(pair -> {
            double average = pair.getValue().stream().mapToDouble(x -> x).average().getAsDouble();
            System.out.printf("%s -> %.2f%n", pair.getKey(), average);
        });
    }
}

I'm pretty sure I'm not doing the sorting by average part correctly but I can't seem to find another way to go about it as the sorting requires an int and the average will always be a double?

Any pointers/explanation will be greatly appreciated!


Solution

  • After taking input you can follow below code snippet

    We can make a new map with name as key and average as value instead of calculating average multiple times

     Map<String, Double> recordsWithAverage = records.entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream().mapToDouble(x -> x).average().getAsDouble()));
    
     recordsWithAverage.entrySet()
                .stream()
                .filter(e -> e.getValue() >= 4.50)
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .forEach(pair -> {
                    System.out.printf("%s -> %.2f%n", pair.getKey(), pair.getValue());
                });