Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stream filtering for best match

My aim is to filter for a best match. In my example I have a list of persons, which I want to filter by surname and firstname.

The matching prescendence would be:

  1. both surname and firstname match, return first match
  2. only surname matches, return first match
  3. none match, throw some exception

My code so far:

final List<Person> persons = Arrays.asList(
  new Person("Doe", "John"),
  new Person("Doe", "Jane"),
  new Person("Munster", "Herman");

Person person = persons.stream().filter(p -> p.getSurname().equals("Doe")).???
like image 979
user871611 Avatar asked Jan 02 '18 11:01

user871611


People also ask

Can we use 2 filters in stream?

2.1. Multiple Filters. The Stream API allows chaining multiple filters. We can leverage this to satisfy the complex filtering criteria described.

What is stream filter?

Stream filter(Predicate predicate) returns a stream consisting of the elements of this stream that match the given predicate. This is an intermediate operation.

How do you filter a list of objects using a stream?

Java stream provides a method filter() to filter stream elements on the basis of given predicate. Suppose you want to get only even elements of your list then you can do this easily with the help of filter method. This method takes predicate as an argument and returns a stream of consisting of resulted elements.

Is parallel stream faster?

The performance of both streams degrades fast when the number of values increases. However, the parallel stream performs worse than the sequential stream in all cases.


2 Answers

Assuming Person implements equals and hashCode:

Person personToFind = new Person("Doe", "Jane");

Person person = persons.stream()
    .filter(p -> p.equals(personToFind))
    .findFirst()
    .orElseGet(() -> 
        persons.stream()
            .filter(p -> p.getSurname().equals(personToFind.getSurname()))
            .findFirst()
            .orElseThrow(() -> new RuntimeException("Could not find person ..."))
    );
like image 131
Cyril Avatar answered Sep 24 '22 07:09

Cyril


You can use

Person person = persons.stream()
        .filter(p -> p.getSurName().equals("Doe"))
        .max(Comparator.comparing(p -> p.getFirstName().equals("Jane")))
        .orElse(null);

It will only consider elements having the right surname and return the best element of them, which is the one with a matching first name. Otherwise, the first matching element is returned.

As already mentioned in a comment, a for loop could be more efficient if there is a best element, as it can short circuit. If there is no best element with matching surname and first name, all element have to be checked in all implementations.

like image 33
Holger Avatar answered Sep 21 '22 07:09

Holger