Search code examples
filterjava-8java-stream

How to filter using streams when we have multiple for loop and collect list out of filtered items from second list stream


I am trying to change the set of code for normal forEach loops to stream and filters. But finding errors in the way it's getting implemented.

Current Code:

List<Long> validFromPersonIds;
List<Person> existingPersonIds;

Person class has two variables:
Long fromPersonId
Long toPersonId;

List<Long> insertionAvailableValidFromPersonIds = new ArrayList<>(validFromPersonIds);
validFromPersonIds.forEach(fromPersonId -> {
    existingPersonIds.forEach(existingPersonId -> {
        if(existingPersonId.getToPersonId() == toPersonId && existingPersonId.getFromPersonId() == fromPersonId)
        {
            insertionAvailableValidFromPersonIds.remove(fromPersonId);
        }
    });
});

In above code I have validFromPersonIds and existingPersonIds list. If any fromPersonId is existing in the table, I dont want to insert to the table. So, i am trying to remove fromPersonId if its already existing in the table.

This is working properly.

Using Streams and Filters, tried code:

List<Long> insertionAvailableValidFromPersonIds = new ArrayList<>();
insertionAvailableValidFromPersonIds = validFromPersonIds.stream()
    .filter(fromPersonId -> existingPersonIds.stream()
            .filter(existingPersonId-> existingPersonId.getToPersonId() == toPersonId && existingPersonId.getFromPersonId() == fromPersonId)
            .collect(Collectors.toList()).contains(fromPersonId).collect(Collectors.toList());

But instead of using normal forEach i am trying to use streams and filters, where I get filtered record and collect it as list. But I am not able to come up with correct syntax for that. Could anyone check where I am going wrong here.


Solution

  • looks like you are filtering by fromPersonId twice.. the first time in the inner filter and then again via this contains. Instead, you can use noneMatch:

    private List<Long> newFilter(List<Long> validFromPersonIds, List<Person> existingPersonIds, long toPersonId) {
       return List<Long> filtered = validFromPersonIds.stream()
          .filter(fromPersonId -> existingPersonIds.stream()
              .noneMatch(eid -> eid.getToPersonId() == toPersonId && eid.getFromPersonId() == fromPersonId)
          )
          .toList();
    }
    

    Updated the answer with a test:

    @Test
    public void test() {
        List<Long> validFromPersonIds = List.of(1l, 2l, 3l, 4l);
        List<Person> existingPersonIds = List.of(
                new Person(1l, 2l)
        );
        long toPersonId = 2l;
    
        assertThat(newFilter(validFromPersonIds, existingPersonIds, toPersonId))
                .containsExactlyElementsOf(currentFilter(validFromPersonIds, existingPersonIds, toPersonId));
    }
    
    // current behavior described in the question
    private List<Long> currentFilter(List<Long> validFromPersonIds, List<Person> existingPersonIds, long toPersonId) {
        List<Long> insertionAvailableValidFromPersonIds = new ArrayList<>(validFromPersonIds);
        validFromPersonIds.forEach(fromPersonId -> {
            existingPersonIds.forEach(existingPersonId -> {
                if (existingPersonId.getToPersonId() == toPersonId
                        && existingPersonId.getFromPersonId() == fromPersonId) {
                    insertionAvailableValidFromPersonIds.remove(fromPersonId);
                }
            });
        });
        return insertionAvailableValidFromPersonIds;
    }