23

Based on some sports results data, I have a Fixture object which has getHome() and getAway() method. I'd like to shorten this method which I've written to only use a single lambda function (instead of creating a new list and two lambdas), is this possible?

    private Collection<FixtureResult> finalResults(Team team) {

    List<FixtureResult>finalResults = new ArrayList<>();

    List<FixtureResult> homeResults = resultList.stream().filter(fixture ->
            fixture.getHome().equals(team))
            .collect(toList());

    List<FixtureResult> awayResults = resultList.stream().filter(fixture ->
            fixture.getAway().equals(team))
            .collect(toList());

    finalResults.addAll(homeResults);
    finalResults.addAll(awayResults);

    return finalResults;
}

5 Answers 5

22

Simple enough

resultList.stream()
        .filter(fixture -> fixture.getHome().equals(team) || fixture.getAway().equals(team)))
        .collect(toList());

EDIT: This is on the assumption that order does not matter to you. If your final list needs to have home result and then away, have a look at Elliott Frisch's answer.

Sign up to request clarification or add additional context in comments.

Comments

16

If you wan to get fancy with lambdas:

Predicate<FixtureResult> isHome = fr -> fr.getHome().equals(team)
Predicate<FixtureResult> isAway = fr -> fr.getAway().equals(team)

resultList.stream()
  .filter(isHome.or(isAway))
  .collect(toList()));

You could even extract the compose predicate to test it in isolation, with no streams involved, which is good for more complex predicates:

Predicate<FixtureResult> isHomeOrAway = isHome.or(isAway)

assertTrue(isHomeOrAway(homeFixture)); 
...

Comments

4

Assuming the order doesn't matter, you can do it on one line. Like,

private Collection<FixtureResult> finalResults(Team team) {
    return resultList.stream()
            .filter(fixture -> fixture.getHome().equals(team) 
                    || fixture.getAway().equals(team))
            .collect(toList());
}

If the order matters (home results and then away), you can do it with a single List like

private Collection<FixtureResult> finalResults(Team team) {
    List<FixtureResult> al = new ArrayList<>(resultList.stream()
            .filter(fixture -> fixture.getHome().equals(team)).collect(toList()));
    al.addAll(resultList.stream()
            .filter(fixture -> fixture.getAway().equals(team)).collect(toList()));
    return al;
}

Comments

4

You can do the following

someStream.filter(((Predicate<SomeClass>) someObject-> someCondition).or(someObject-> someOtherCondition))

Or you can define your own "or" function that won't cause such a deep hierarchy

@SuppressWarnings("unchecked")
<R> Predicate<R> or(Predicate<R> ...predicates) {
   return r -> Arrays.stream(predicates).anyMatch(p -> p.test(r));
}

That gives you a cleaner interface without casting and the nesting

.filter(or(
      yourObject -> {
          return false;
      },
      yourObject -> {
          return false;
      },
      yourObject -> {
          return false;
      },
      yourObject -> {
          return false;
      }
 ))

1 Comment

This is a gem hidden at bottom of the page! Hopefully more developers realise the elegance of these types of solutions.
2

You can simply create a conditions concatenations or can concatenate multiple filter call

Conditions concatenations

myList.stream()
      .filter(element -> (condition1 && condition2 && condition3))

Multiple filter call

myList.stream()
      .filter(element -> condition1)
      .filter(element -> condition2)
      .filter(element -> condition3)

1 Comment

Each next filter call applies to filtered results. The author most probably wanted OR filter conditions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.