2

Do Java streams have a convenient way to map based upon a predicate, but if the predicate is not met to map to some other value?

Let's say I have Stream.of("2021", "", "2023"). I want to map that to Stream.of(Optional.of(Year.of(2021)), Optional.empty(), Optional.of(Year.of(2023))). Here's one way I could do that:

Stream<String> yearStrings = Stream.of("2021", "", "2023");
Stream<Optional<Year>> yearsFound = yearStrings.map(yearString ->
    !yearString.isEmpty() ? Year.parse(yearString) : null)
        .map(Optional::ofNullable);

But here is what I would like to do, using a hypothetical filter-map:

Stream<String> yearStrings = Stream.of("2021", "", "2023");
Stream<Optional<Year>> yearsFound = yearStrings.mapIfOrElse(not(String::isEmpty),
    Year::parse, null).map(Optional::ofNullable);

Of course I can write my own mapIfOrElse(Predicate<>, Function<>, T) function to use with Stream.map(), but I wanted to check if there is something similar in Java's existing arsenal that I've missed.

1
  • afaik there is nothing like that, though for this situation might be simpler just to wrap the parse method in your own that does the check, and returns null if the String is empty, which is what you're doing in the first example, just method references look nicer for longer logic imo. Commented Sep 9, 2022 at 20:19

3 Answers 3

1

There is not a very much better way of doing it than you have it - it might be nicer if you extracted it to a method, but that's really it.

Another way might be to construct Optionals from all values, and then use Optional.filter to map empty values to empty optionals:

yearStreams.map(Optional::of)
           .map(opt -> opt.filter(Predicate.not(String::isEmpty)));

Is this better? Probably not.

Yet another way would be to make use of something like Guava's Strings.emptyToNull (other libraries are available), which turns your empty strings into null first; and then use Optional.ofNullable to turn non-nulls and nulls into non-empty and empty Optionals, respectively:

yearStreams.map(Strings::emptyToNull)
           .map(Optional::ofNullable)
Sign up to request clarification or add additional context in comments.

Comments

0

You can just simply use filter to validate and then only map

Stream<Year> yearsFound = yearStrings.filter(yearString->!yearString.isEmpty()).map(Year::parse)
    

1 Comment

The requirement in the question was not to filter them out, instead map them to null.
0

It's hardly possible to combine all these actions smoothly in well-readable way within a single stream operation.

Here's a weird method-chaining with Java 16 mapMulti():

Stream<Optional<Year>> yearsFound = yearStrings
    .mapMulti((yearString, consumer) -> 
        Optional.of(yearString).filter(s -> !s.isEmpty()).map(Year::parse)
                .ifPresentOrElse(year -> consumer.accept(Optional.of(year)),
                    () -> consumer.accept(Optional.empty()))
    );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.