iCodeLikeImDrunk
iCodeLikeImDrunk

Reputation: 17806

How do I filter through a list of objects that contains lists?

Imagine I have a json like the following:

[
    {
        field: string,
        roles: [
            {
                id: int,
                providedFor: int
            },
            {
                id: int,
                providedFor: int
            },
            ...
        ]
    },
    {
        field: string,
        roles: [
            {
                id: int,
                providedFor: int
            },
            {
                id: int,
                providedFor: int
            },
            ...
        ]
    },
    {
        field: string,
        roles: [
            {
                id: int,
                providedFor: int
            },
            {
                id: int,
                providedFor: int
            },
            ...
        ]
    }
]

And this is parse into a list of objects and call this list providers. How can I go about filtering/streaming this list, find the correct provider then breaking out?

I've tried something like the following:

providers.parallelStream().filter(p -> p.getRoles().parallelStream().filter(r -> r.getProvidedFor.equal("something")).findFirst());

Upvotes: 0

Views: 2890

Answers (1)

wassgren
wassgren

Reputation: 19211

Since you have not provided any classes I have experimented with the following structure:

class Field {
    String field;
    List<Role> roles;

    Field(String field, List<Role> roles) {
        this.field = field;
        this.roles = roles;
    }
}

class Role {
    int id;
    int providedFor;

    Role(int id, int providedFor) {
        this.id = id;
        this.providedFor = providedFor;
    }
}

Field is the outer object and Role is the inner object. So, if you build up a List of Field objects like this:

final List<Field> fields = Arrays.asList(
        new Field("f11", Arrays.asList(new Role(11, 11), new Role(11, 12))),
        new Field("f22", Arrays.asList(new Role(22, 22), new Role(22, 23))),
        new Field("f33", Arrays.asList(new Role(33, 33), new Role(33, 34))));

And, you are searching for something:

int something = 22;

You can filter out your Role by using the flatMap method like this:

final Optional<Role> first = fields.stream()
        .flatMap(f -> f.roles.stream())          // Chain all "substreams" to one stream
        .filter(r -> r.providedFor == something) // Check for the correct object
        .findFirst();                            // Find the first occurrence

Note that if providedFor is not a primitive but some kind of Object the equals method should be used instead.

If you are instead looking for the Field class you can simply nest the streams like this:

final Optional<Field> firstField = fields.stream()
        .filter(f -> f.roles.stream()                       // Nested stream
                .anyMatch(r -> r.providedFor == something)) // Return true if present
        .findFirst();                                       // Find the first Field object (findAny can be used here as well if "any" hit is ok)

This will return the first Field that matches the provided Role.providedFor.

From the flatMap JavaDocs:

Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.

Basically you are chaining nested streams to one stream.

In the end, the findfirst method returns the first match. This means that there may be no match and therefore an Optional is returned. The Optional class has method for retrieving the underlying object and checking if it exists (get() and isPresent()). Check out the Optional JavaDocs for more info.

Upvotes: 3

Related Questions