Get element from list that contains another list - java

I have this configuration with java:
Student {
private List<Note> notes;
}
Note {
private int value;
// Constructor - getters - setters
}
School {
Private List<Student> students;
// Constructor - getters - setters
}
I want the following behavior:
Students :
S1 : note1(value=10), note2(value=16)
S2 : note1(value=7), note2(value=18), note3(value=2)
S3 : note1(value=19)
I want to manage an object with a list of schools as:
Manage {
private List<School> schools;
}
And I want to get the school who has the student with the higher note.
In this example: the result would be S3 because we have one student with the higher note 19.
How can I achieve this behavior using Java Stream?

You can create a Stream<Map.Entry<School,Student>> of all the pairs of Schools and Students, and then find the entry having the Student with the max value.
For that purpose I suggest adding to Student class a getMaxValue() method that would return the max value of all the Students Notes.
Optional<School> school =
schools.stream()
.flatMap(sc -> sc.getStudents()
.stream()
.map(st -> new SimpleEntry<>(sc,st)))
.collect(Collectors.maxBy(Comparator.comparing(e -> e.getValue().getMaxValue())))
.map(Map.Entry::getKey);

Related

Stream - filter based on hashMap value

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());
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
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.
A step-by-step way of thinking:
We need to filter projects based on the criteria that these have at least one author (student) who has passed a specific course (courseId) with a grade >= 2 (another filter).
dipProjects.stream().filter(p->p.getAuthors().stream().anyMatch(s->s.getCourseList().getOrDefault(courseId,0) >= grade)).map(p->p.getTitle()).collect(Collectors.toList());

Use of Predicate in comparing two list of different type in Java

I have two list of type String and an object (consider Employee). String type list have employee codes. Here I need to check if Employee list have any object of code(attribute) saved in String. Below is my employee class
public class Employee {
public String code;
public String id;
// getters, setters and constructor
}
Here I am able to find whether employees have code saved in the given String List (employeeUserGrpCodes).
public static void main(String[] args) {
final List<String> employeeUserGrpCodes= Arrays.asList("ABCWelcome","ABCPlatinum","SuperEmployee");
List<Employee> empList=new ArrayList<Employee>();
Employee k1= new Employee("KCAEmployee","1");
Employee k2 = new Employee("ABCWelcome","2");
empList.add(k1);
empList.add(k2);
List<Employee> empListN = empList.stream().filter(i->employeeUserGrpCodes.stream().anyMatch(j->j.equalsIgnoreCase(i.getCode()))).collect(Collectors.toList());
List<String>newEmpList = empList.stream().map(a->a.getCode()).collect(Collectors.toList()).stream().filter(employeeUserGrpCodes::contains).collect(Collectors.toList());
if(!empListN.isEmpty() || !newEmpList.isEmpty())
{
System.out.println("Employee have employeeUserGrpCodes");
}
}
In the above code, both approaches are working that is List 'empListN' and List 'newEmpList'. Is it possible to do the same with the help of Predicates which I can easily put in String 'anymatch' like
Predicate<Employee> isEmpUserGroup = e -> e.getCode().equalsIgnoreCase(employeeUserGrpCodes.stream())
boolean isRequiredEmployee = empList.stream().anyMatch(isEmpUserGroup);
First of all for the purpose of knowing if Employee have employeeUserGrpCodes you don't need the two lists because is empListN is not empty newEmpList won't be as well, so we can use only of the two lists, and then, related with the use of the predicates, you are using them already in the filter expressions, you can have something like this for the empListN list:
Predicate<Employee> employeePredicate = e -> employeeUserGrpCodes.stream().anyMatch(c -> c.equalsIgnoreCase(e.getCode()));
List<Employee> empListN = empList.stream().filter(employeePredicate).collect(Collectors.toList());
You can notice that the Predicate is using another predicate as well
c -> c.equalsIgnoreCase(e.getCode())
So you can also replace the if condition and avoid using a temporary list if you test your predicate against the employee list like this:
if (empList.stream().anyMatch(employeePredicate)) {
System.out.println("Employee have employeeUserGrpCodes");
}

Java streams - getting a map from list of maps

I have a class University that contains (among other things) a list of semesters and a method:
public Map<String,Integer> gradesMap(Student s,Semester s)
that should return a map of grades for a given student in a given semester.
Classes Semester and Course look something like this:
public class Semester{
...
private List<Course> courses;
public List<Courses> getCourses(){return courses;}
...
}
public class Course{
...
String courseName;
private Map<Student,Integer> courseGrades;
public Map<Student,Integer> getCourseGrades(){return courseGrades;}
public String getCourseName(){return courseName;}
...
}
I tried writing something like:
Map<String,Integer> grades=semester.getCourses().stream().
forEach(c->c.getCourseGrades()).collect(Collectors.toMap(key,value));
but I'm not sure how to fetch the key and value for my map. Any suggestions would be appreciated.
Edit: Output map should contain a Course name and a grade.
You can get the course grade from the Course Grade map by the student and collect grade as the map's value and course name as the key.
Map<String,Integer> gradesMap =
semester.getCourses()
.stream()
.collect(Collectors.toMap(c -> c.getName(),
c -> c.getCourseGrades().get(studentObj)));
Note: Make sure you defined equals() and hashCode() for Student

Find max size of an element among list of objects

#Data
class Person {
private String fname;
private String lname;
private List<String> friends;
private List<BigDecimal> ratings;
...
}
#Data
class People {
private List<Person> people;
...
}
suppose i have the above classes, and need to figure out what is the most number of friends any one person has. I know the pre-streams way of doing it... by looping over every element in the list and
checking if friends.size() is greater than the currently stored number of longest list. is there a way to streamline the code with streams? something like this answer?
Compute the max Person contained in People according to a comparator that relies on the size of Person.getFriends() :
Optional<Person> p = people.getPeople()
.stream()
.max(Comparator.comparingInt(p -> p.getFriends()
.size()));
Note that you could also retrieve only the max of friends without the Person associated to :
OptionalInt maxFriends = people.getPeople()
.stream()
.mapToInt(p -> p.getFriends()
.size())
.max();
You can declare the following method in Person class:
public int getNumberOfFriends(){
return friends.size();
}
and then use it in a stream like this:
Optional <Person> personWithMostFriends = people.stream().max(Comparator.comparingInt(Person::getNumberOfFriends));
With this approach you will get the Person object with the most friends, not only the maximum number of friends as someone suggested.
Your question already answered. but i add this for one thing. if your list of person size large and if you have multi-core pc and you want to used this efficiently then use parallelstream().
To get person:
Person person = people.getPeople()
.parallelStream()
.max(Comparator.comparingInt(p-> p.getFriends().size()))
.orElse(null);
To get size:
int size = people.getPeople()
.parallelStream()
.mapToInt(p -> p.getFriends().size())
.max().orElse(0);

How to write my own comparator class in java?

I didn't find proper solution for the below scenario. I have employee names and location. In each location many employees can work.
Example: assume that employee names are unique so I consider it as a key and value as location.
TreeMap<String,String> t=new TreeMap<String,String>();
t.put(mike, Houston);
t.put(arian, Houston);
t.put(John, Atlanta);
Well my scenario is i have to write my own comparator where location is sorted first and when there are multiple locations of same name then they need to be sorted by employees. Any kind of help is appreciated.
you need a structure, and compareTo:
public class EmpLoc implements Comparable<EmpLoc> {
String employee;
String location;
public EmpLoc (String _employee, String _location)
{
employee=_employee;
location=_location;
}
#Override
public int compareTo(EmpLoc other)
{
int last = this.location.compareTo(other.location);
return last == 0 ? this.employee.compareTo(other.employee) : last;
}
}
The problem is in your data structure. TreeMap ensure your keys are always sorted in an order, but your key doesn't have full information you need to sort. Instead what you need is probably
TreeSet<Employee> employees = new TreeSet<>(employeeComparator);
where Employee is:
public class Employee {
private String name;
private String location;
/* getters & setters omitted */
}
Now you can create a comparator for Employee
You can use similar structure:
Map<String, List<String>> map = new TreeMap<>(<your_own_comparator_for_locations_or_default_one>);
This is Multimap, and this is implementation by conventional means, but also there are third-party implementation, e.g. Guava. Guava has some sorted, synchronized and immutable implementations of multimaps, you can use them by default or to see how to do some things.
You can put values like below:
public void putEmployees(String location, String employee) {
List<String> employees = map.get(location);
if (employee == null) {
employees = new ArrayList<>();
}
employees.add(employee);
Collections.sort(employees, <your_own_comparator_for_employees_or_default_one>);
map.put(location, employees);
}

Categories