Filter Flow Using Correlation Between Flow Elements

Suppose class a is Personas follows:

public class Person {
    private int id;
    private String discriminator;
    // some other fields plus getters/setters
}

Now I have elements Streamfrom Person, and this stream can contain several instances Personthat have the same id, but different values discriminator, i.e.[Person{"id": 1, "discriminator": "A"}, Person{"id": 1, "discriminator": "B"}, Person{"id": 2, "discriminator": "A"}, ...]

What identifier should do is filter out all instances Personwith some identifier if at least one instance Personwith this identifier has a certain discriminator value. So, continuing the example described above, filtering by the value of discriminator "A" will lead to an empty collection (after the reduction operation, of course) and filtering by the value of discriminator "B" will give a collection that does not contain an instance Personwith an identifier equal to 1.

I know that I can reduce the flow using groupingBycollector and group elements with Person.id, and then remove the display from the result Mapif the matched list contains an element Personwith the specified discriminator value, but I'm still wondering if there is an easier way to achieve the same result?

+4
source share
3 answers

If I understand your problem correctly, you will first find everything IDthat matches the discriminator:

Set<Integer> ids = persons.stream()
       .filter(p -> "A".equalsIgnoreCase(p.getDiscriminator()))
       .map(Person::getId)
       .collect(Collectors.toSet())

And then delete the entries that match these:

persons.removeIf(x -> ids.contains(x.getId()))
+3
source

Eugenes answer works fine, but I personally prefer one expression. So I took his code and combined it into one operation. Which looks like this:

final List<Person> result = persons.stream()
    .filter(p -> "B".equalsIgnoreCase(p.getDiscriminator()))
    .map(Person::getId)
    .collect(
        () -> new ArrayList<>(persons),
        ( list, id ) -> list.removeIf(p -> p.getId() == id),
        ( a, b ) -> {throw new UnsupportedOperationException();}
    );

, , persons, null.

SideNote. UnsupportedOperationException .

+1

So below I present the solution that I came up with. First, I group the collection / stream of Person as an attribute of Person.id, and then pass it on the elements of the map and filter out those that have at least one value that matches this discriminator.

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        List<Person> persons = Arrays.asList(
            new Person(1, "A"),
            new Person(1, "B"),
            new Person(1, "C"),
            new Person(2, "B"),
            new Person(2, "C"),
            new Person(3, "A"),
            new Person(3, "C"),
            new Person(4, "B")
        );

        System.out.println(persons);
        System.out.println(filterByDiscriminator(persons, "A"));
        System.out.println(filterByDiscriminator(persons, "B"));
        System.out.println(filterByDiscriminator(persons, "C"));
    }

    private static List<Person> filterByDiscriminator(List<Person> input, String discriminator) {
        return input.stream()
            .collect(Collectors.groupingBy(Person::getId))
            .entrySet().stream()
            .filter(entry -> entry.getValue().stream().noneMatch(person -> person.getDiscriminator().equals(discriminator)))
            .flatMap(entry -> entry.getValue().stream())
            .collect(Collectors.toList());
    }

}

class Person {

    private final Integer id;
    private final String discriminator;

    public Person(Integer id, String discriminator) {
        Objects.requireNonNull(id);
        Objects.requireNonNull(discriminator);
        this.id = id;
        this.discriminator = discriminator;
    }

    public Integer getId() {
        return id;
    }

    public String getDiscriminator() {
        return discriminator;
    }

    @Override
    public String toString() {
        return String.format("%s{\"id\": %d, \"discriminator\": \"%s\"}", getClass().getSimpleName(), id, discriminator);
    }
}
+1
source

Source: https://habr.com/ru/post/1689210/


All Articles