Home > Mobile >  Stream - filter based on hashMap value
Stream - filter based on hashMap value

Time:08-07

I want to start from a collection of diploma projects and by using stream I want to get an arrayList of diploma project titles, from the students that have taken a course identified by courseId. They will also need to have passed the course with grade of 2 or higher.

I have this DiplomaProject class:

public class DiplomaProject{
    String title;
    ArrayList<Student> authors
}

Each diplomaProject can have multiple authors.

I have this Course class:

public class Course{
    String courseName;
    String courseId;
}

This Student class:

public class Student{
    String name;
    HashMap<Course, Integer> courseList;
    DiplomaProject diplomaProject;
}

The grade of the course is the Integer value of courseList.

This is my current solution, but I know it does not do what I want. I can't find a way to filter based on the value of the courseList, and I do not know how I can get the the diplomaProject titles at the end of the streams (only at the top level).

public static List<String> diplomaProjectTitle(List<DiplomaProject> diplomaProjects) {
        return diplomaProjects.stream()
                .map(diplomaProject -> diplomaProject.authors)
                .flatMap(students -> students.stream())
                .filter(student -> student.courseList.keySet().equals("math1001"))
                .flatMap(student -> student.courseList.keySet().stream())
                .map(student -> student.courseName)
                .collect(Collectors.toList());

CodePudding user response:

You are losing the info on the diploma projects with the the .map functions. What you want to do is operate within the .filter() functions of the first diplomaproj stream. Therefore


    public List<String> diplomaProjectTitles(List<DiplomaProject> diplomaProjects) {
        return diplomaProjects.stream()
                .filter(projects -> projects.getAuthors().stream().map(Student::getCourseList)
                        //get the course matching this one (both name and id)
                        .map(c -> c.get(new Course("math101", "1")))
                        //check if that course has grade greater than the minimum
                        .anyMatch(grade -> grade>=2))
                .map(DiplomaProject::getTitle)
                .collect(Collectors.toList());
    }

For this to work though you would need to modify your Course class. Since you are using it within a hash map as a key, and want to get it through a custom query you will need to add the hashCode() function.

public class Course {
    private String courseName;
    private String courseId;

    @Override
    public int hashCode() {
        return courseName.hashCode()   courseId.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if(o instanceof Course oc) {
            return oc.getCourseName().equals(this.getCourseName()) && oc.getCourseId().equals(this.getCourseId());
        }
        return false;
    }
    
    //getters and setters
}

In order to test it I created a simple method that prepares a test case

public void filterStudents() {
        List<DiplomaProject> diplomaProjects = new ArrayList<>();
        List<Course> courses = new ArrayList<>();
        courses.add(new Course("math101", "1"));
        courses.add(new Course("calc101", "2"));
        courses.add(new Course("calc102", "3"));

        List<Student> students = new ArrayList<>();
        Map<Course, Integer> courseMap = Map.of(courses.get(0), 3, courses.get(1), 1);
        students.add(new Student("TestSubj", courseMap));
        Map<Course, Integer> courseMap2 = Map.of(courses.get(0), 1, courses.get(1), 3);
        students.add(new Student("TestSubj2", courseMap2));
        diplomaProjects.add(new DiplomaProject("Project1", students));
        diplomaProjects.add(new DiplomaProject("Project2", List.of(students.get(1))));

        log.info("Dimploma projects are "   diplomaProjectTitles(diplomaProjects));

    }

this way Project 1 will have a student with math101 with grade 3 and one with grade 1, and Project2 will have a student with math101 with grade 1. As expected, the result of the filtering method is only project1

CodePudding user response:

I want to get a List of diploma project titles, from the students that have taken a Course identified by the given courseId. They will also need to have passed the course with grade of 2 or higher.

In your method diplomaProjectTitle you're actually losing the access to the titles of the diploma projects at the very beginning of the stream pipe-line because the very first operation extracts authors from the stream element.

You need to need the stream to of type Stream<DiplomaProject> in order to get a list of diploma project titles as a result. Therefore, all the logic needed to filter the desired diploma project should reside in the filter() operation.

That's how it might be implemented:

public static List<String> diplomaProjectTitle(List<DiplomaProject> diplomaProjects,
                                               String courseId,
                                               Integer grade) {
    return diplomaProjects.stream()
        .filter(diplomaProject -> diplomaProject.getAuthors().stream()
            .anyMatch(student -> 
                student.getCourseList().getOrDefault(courseId, 0) >= grade
            )
        )
        .map(DiplomaProject::getTitle)
        .toList();   // or .collect(Collectors.toList()) for JDK version earlier than 16
}

A couple of important notes:

  • Avoid using public fields and accessing the fields from outside the class directly rather than via getters.

  • Pay attention to the names of your method, variables, etc. The name courseList is confusing because it's actually not a List. This map should rather be named gradeByCourse to describe its purpose in a clear way.

  • Leverage abstractions - write your code against interfaces. See What does it mean to "program to an interface"?

  • Pay attention to the types you're working with keySet().equals("math1001") even IDE is capable to tell you that something is wrong here because Set can never be equal to a String.

  • Related