Home > Software design >  Obtain a Value mapped to the Key that matches the Predicate from a List of Map using Streams
Obtain a Value mapped to the Key that matches the Predicate from a List of Map using Streams

Time:09-13

I have a list of maps mapList.

I'm iterating using nested for-loops to get a value.

Here's my imperative code:

  for(Map<String, Object> stringObjectMap : mapList){
    for(Map.Entry<String, Object> entry : stringObjectMap.entrySet()){
      if("SubmittedBy".equalsIgnoreCase(entry.getKey().trim())){
          // Logics to implement
      }
    }
  }

Now, I'm trying to achieve it using Stream API.

I need to get a String value from one of the Maps which is associated with a key matches ignoring case to the string "SubmittedBy".

Here's my attempt:

String submittedBy = mapList.stream()
    .filter(map -> map.entrySet().stream()
    .filter(sub -> sub.getKey().equalsIgnoreCase("SubmittedBy")))
    .findAny()
    .get();

It produces a compile error saying bad return type. How I can fix it?

Bad return type in lambda expression:
Stream<Entry<String, Object>> cannot be converted to boolean

CodePudding user response:

It seems like you want to obtain a value mapped to the key that matches "SubmittedBy".

You've messed around with your filters, and you don't need a nested stream in the first place.

Initially, you have a stream of maps Stream<Map<String,Object>>.

As the first step, you need to flatten the entries of each map using flatMap() operation to obtain a stream of entries.

And only then you need to apply a filter(). Operation findFirst() would give an optional of map-entry and to transform it into the Optional<String> you can map use of Optional.map().

String submittedBy = mapList.stream()                                // Stream<Map<String, Object>>
    .flatMap(map -> map.entrySet().stream())                         // Stream<Map.Entry<String, Object>>
    .filter(entry -> entry.getKey().strip().equalsIgnoreCase("SubmittedBy"))
    .findAny()                                                      // Optional<Map.Entry<String, Object>>
    .map(entry -> (String) entry.getValue())                        // Optional<String>
    .orElseThrow();

Note that Optional.orElseThrow() is preferred over Optional.get() when you're not checking the presence of data in the Optional (see the API Note).

Both methods would throw NoSuchElementException in case if optional is empty, by using orElseThrow() you're making your intention (throw if there's no result) clear to the reader of the code.


Regarding the compilation error, as you probably know, Predicate is a function that produces a boolean value.

And to produce a value from a stream, you need to apply a terminal operation. anyMatch(), allMatch(), noneMatch() - these are terminal operations that return a boolean.

On the other hand, filter() is an intermediate operation, which means it produces another stream, not a value.

For more information have a look at API documentation Stream operations and pipelines.

  • Related