delita
delita

Reputation: 1581

lambda collecting within a ifPresent

How can I use the collect method to collect within a nested ifPresent method?

So far this is what I have:

List result = list.stream()
                  .findFirst()
                  .ifPresent(info -> {
                      info.getMap()
                          .stream()
                          .filter( entry -> entry.getKey().equals("test"))
                          .map(entry::getValue)
                          .collect(Collectors.toList())                          
                   });

Upvotes: 2

Views: 5708

Answers (3)

Lino
Lino

Reputation: 19926

You might wanted to do this:

List result = list.stream()
    .limit(1)
    .flatMap(info -> // <-- removed brace {
        info.getMap().stream()
            .filter( entry -> entry.getKey().equals("test"))
            .map(entry::getValue)
    ) // <-- removed brace here too
    .collect(Collectors.toList());

Let me explain:

.limit(1): limits the stream only to the first element (or returns an empty stream if initial stream is empty)

.flatMap(): maps the stream into a new stream. in this case a new stream consisting of values from Entry#getValue() is returned

.collect(Collectors.toList()): at last the stream of values is collected into a list.

As Olivier Grégoire said in the comments, the following does work too:

List result = list.stream()
    .limit(1)
    .flatMap(info -> info.getMap().stream())
    .filter( entry -> entry.getKey().equals("test"))
    .map(entry::getValue)
    .collect(Collectors.toList());

Which in my opinion is more readable and clearer indicates what the intents were.

Upvotes: 4

Rik
Rik

Reputation: 828

It looks like you have a List<Info> containing zero or more Infos, where Info is some object which has a getMap() method returning a Map<String, Something> and you want to end up with a List<Something>, which is all the values of the first list item's map which have the key test.

You want to map the optional instead of using ifPresent. ifPresent is a terminal operation which doesn't return anything (just executes code).

list // List<Info>
    .stream() // Stream<Info>
    .findFirst() // Optional<Info>
    .map(info -> info.getMap().entrySet() // Set<Entry<String, Something>>
         .stream() // Stream<Entry<String, Something>>
         .filter(entry -> entry.getKey().equals("test"))
         .map(Entry::getValue) // Stream<Something>
         .collect(toList()) // List<Something>
    ) // Optional<List<Something>>
    .orElse(Collections::emptyList) // List<Something>

If last line not specified then you'll be getting an Optional<List<Info>> back.

EDIT: Lino's answer with limit is more idiomatic, pick that one unless you really want to use Optional in this way.

Edit 2:

Assuming your code above is your complete business logic, you probably just to find the value of "test" in the first item in your list (thanks Lino for limit). That'd be:

list.stream() // Stream<List<Info>>
    .limit(1) // Stream<List<Info>> count 0/1
    .flatMap(info::getMap) Stream<Map<String, Something>> 0/1
    .map(map -> map.get("test")) Stream<Something> 0/1, possibly null
    .filter(Objects::nonNull) Stream<Something> 0/1
    .findFirst() Optional<Something>

For completeness, if you wanted to find ANY values of "test" in maps in your list, it'd be:

list.stream() // Stream<List<Info>>
    .flatMap(info::getMap) Stream<Map<String, Something>> 
    .flatMap(map -> map.entrySet().stream()) // Stream<Entry<String, Something>>
    .filter(entry -> entry.getKey().equals("test"))
    .map(Entry::getValue) // Stream<Something>
    .collect(toList()) // List<Something>

Upvotes: 1

Eran
Eran

Reputation: 393781

Assuming info.getMap returns a java.util.Map, I don't see why you need the inner stream pipeline at all.

Just use get("test") to obtain the value of the "test" key:

List result = 
    list.stream()
        .map(info -> info.getMap().get("test"))
        .filter(Objects::nonNull)
        .limit(1)
        .collect(Collectors.toList()); 

P.S., don't use a raw type. You should change the type of the returned list to List<ValueType> where ValueType is the type of values in the info.getMap() Map.

Upvotes: 3

Related Questions