Fllappy
Fllappy

Reputation: 401

Filter operation via Stream and maintain as map instead of Stream

I am currently getting a map data from an external api call.
I want to ensure the data is not null or empty and perform a set of operations on it
by filtering to a specific key in the map and capturing results into another object.

The key itself is comma separated.

Example key / value in map.

"key1,key2,key3,id100" : {
     "val1: "",
     "val2: "",
     "val3: "",
     ... others
}

I am filtering to capture all values under this key (so data cal1, val2, val3 and others) and then perform some operations.

But when I perform the filter as shown, I end up with a stream.

Thus Instead of just a Map<String, Object>, I end up with Stream<Map.Entry<String, Object>>.

Tried flatmap and getting following error:

no instance(s) of type variable(s) U exist so that Stream<Entry<String, Object>> conforms to Optional

How could I convert it back to a Map from the Stream or a better way to filter this? Thanks.

Could have just done this via a for loop without Streams but trying to see how
I could achieve this in a Stream implementation thus not looking for a for loop solution. Please advice. Thanks.

private NewObject get() {

    Map<String, Object> data = // data filled in by an external rest call;

    return Optional.ofNullable(data)

        // using flatmap here throws above error
        .map(Map::entrySet)
        .map(entries -> entries.stream()
                .filter(entry -> entry.getKey().contains("id100))

        // I wish to carry on further operations from here by using the filtered map. 
        // having issues cos capturedData is a Stream 

        // even if using flatmap at this stage, capturedData is still a Stream.
        // wanting to do the following but can't due to it being a Stream and not a map 
        ).map(capturedData -> {
            Map<String, Object> at = (Map<String, Object>) capturedData;

            NewObject newObject = new NewObject();
            newObject.setName((String) at.get("val1"));

            return newObject;

        }).orElse(null);
}

Upvotes: 3

Views: 99

Answers (2)

aksappy
aksappy

Reputation: 3400

This code below filters the entryset in data, collects it to a set before performing the next set of operations. findFirst is used so that there is only ever a single entry to deal with.

Optional.ofNullable(data)
                .map(Map::entrySet)
                .map(entries -> 
                      entries
                        .stream()
                        .filter(e -> e.getKey().contains("id1000")).collect(Collectors.toSet()))
                .stream()
                .findFirst()
                .map(capturedData -> {
                    Map<String, Object> map = (Map<String, Object>) capturedData;
                    NewObject newObject = new NewObject();
                    newObject.setName((String) at.get("val1"));
                    return newObject;
                })
                .orElse(null);

Upvotes: 1

Thiyagu
Thiyagu

Reputation: 17920

Use map to construct the NewObject and use findFirst to get the first value (as per your comment, there will be only one entry whose key has substring id100). Finally use flatMap to unwrap the Optional<NewObject>.

return Optional.ofNullable(data)
    .map(Map::entrySet)
    .flatMap(entries -> entries.stream()
            .filter(entry -> entry.getKey().contains("id100"))
            .map(entry -> {
                NewObject newObject = new NewObject();
                Map<String, String> nestedMap = (Map<String, String>) entry.getValue();
                newObject.setName(nestedMap.get("val1"));
                return newObject;
            })
            .findFirst())
    .orElse(null);

Upvotes: 2

Related Questions