Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply multiple filter to a map in Java [duplicate]

Based on the following question: filter Map in Java 8 Streams

public void filterStudents(Map<Integer, Student> studentsMap){
    Map<Integer, Student> filteredStudentsMap = 
        studentsMap.entrySet()
                   .stream()
                   .filter(s -> s.getValue().getAddress().equalsIgnoreCase("delhi"))
                   .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

This filter students leaving in dehli. How could I filter students leaving in dehli, amsterdam or new york?

Is there a better way than filtering three times the original map and merging the three outputs together?

like image 421
Nakrule Avatar asked Nov 22 '25 03:11

Nakrule


2 Answers

There is Predicate#or(Predicate) to logically compose two Predicates.

Predicate<Student> livesInDelhi = student -> "delhi".equalsIgnoreCase(student.getAddress());
Predicate<Student> livesInAmsterdam = student -> "amsterdam".equalsIgnoreCase(student.getAddress());
Predicate<Student> livesInNewYork = student -> "new york".equalsIgnoreCase(student.getAddress());

Predicate<Student> livesInAnyOfTheseThreeCities = livesInDelhi.or(livesInAmsterdam).or(livesInNewYork);

A filter call would look like

.filter(e -> livesInAnyOfTheseThreeCities.test(e.getValue()))

How could I adapt the fourth lines where you're chaining filtering parameters?

Assuming we have an array of cities

final String[] cities = {"delhi", "amsterdam", "new york"};

for each Student, we could write a Predicate<Student> and reduce them by Predicate::or

Predicate<Student> livesInAnyOfGivenCities = 
  Arrays.stream(cities)
    .map(city -> (Predicate<Student>) student -> city.equalsIgnoreCase(student.getAddress()))
    .reduce(Predicate::or)
    .orElseGet(() -> student -> false);

student -> false is used when there are no cities given.

like image 96
Andrew Tobilko Avatar answered Nov 24 '25 17:11

Andrew Tobilko


Of course, you should use plain old object oriented programming here and create a separate method with meaningful name:

public void filterStudents(Map<Integer, Student> studentsMap){
Map<Integer, Student> filteredStudentsMap = 
    studentsMap.entrySet()
               .stream()
               .filter(s -> s.getValue().liveIn("delhi", "amsterdam", "new york"))
               .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

Of course this requires creating a corresponding method in Student class, but why else do we need objects and OOP?)

public class Student {

   // other methods of Student

   public boolean liveIn(String... cities) {
     return Arrays.stream(cities).anyMatch(this.city::equals);
   }
}

Array is just for example - you can use set, list or whatever you want. The point here is to create a meaningful methods that could be used in stream api.

like image 37
star67 Avatar answered Nov 24 '25 17:11

star67