amaembo / streamex

Enhancing Java Stream API
Apache License 2.0
2.18k stars 249 forks source link

Add present() method to filter and extract Optional values #249

Closed stickfigure closed 3 years ago

stickfigure commented 3 years ago

I find myself doing this a lot when working with streams of optionals:

someStreamOfOptionals
    .filter(Optional::isPresent)
    .map(Optional::get)
    ...

This boilerplate seems common enough that it would be convenient to have a present() method:

someStreamOfOptionals
    .present()
    ...

Thoughts?

stickfigure commented 3 years ago

To give a real-world example of some code I'm trying to improve:

    return StreamEx.of(shop.getLanguages())
        .map(Language::valueOfOpt)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .map(lang -> getListingTranslation(shopId, listingId, lang))
        .filter(Optional::isPresent)
        .map(Optional::get)
        .toImmutableList();
loordgek commented 3 years ago

can you use java 9?

stickfigure commented 3 years ago

Not yet (for a variety of Google App Engine related reasons). But in Java 9, what's the idiom that cleans this up?

loordgek commented 3 years ago

in j9 there is Optional.stream so you can do someStreamOfOptionals.flatmap(optional::stream)

amaembo commented 3 years ago

Adding .present() is not very good idea, as this method will be available for any stream, not just stream of optional, and its meaning will be confusing. People may accidentally call it expecting some different behavior. The same behavior is already provided with .mapPartial(fn) method, so you can use

streamOfOptionals.mapPartial(x -> x) // or use Function.identity()

Another (though possibly less performant) way to achieve this is to use StreamEx.of(Optional):

streamOfOptionals.flatMap(StreamEx::of)

This solution could be useful if the streamOfOptionals is not StreamEx object but a classic JDK stream.

amaembo commented 3 years ago

Your bigger sample could be reduced to:

    return StreamEx.of(shop.getLanguages())
        .mapPartial(Language::valueOfOpt)
        .mapPartial(lang -> getListingTranslation(shopId, listingId, lang))
        .toImmutableList();
stickfigure commented 2 years ago

Looks like mapPartial is exactly what I am looking for, thanks!