Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to join two Optional<String> with a delimiter in Java 8

I have two Optional strings, name1 and name2. I want to join the two such that the result is also an Optional with:

  1. If either one is non-empty, the result should be the non-empty name.
  2. If both are non-empty, I want the result to be joined with the delimiter AND.
  3. If both are empty, the result should be an empty Optional

My attempt at this:

StringBuilder sb = new StringBuilder();
name1.ifPresent(sb::append);
name2.ifPresent(s -> {
    if (sb.length() > 0) {
        sb.append(" AND ");
    }
    sb.append(s);
}
Optional<String> joinedOpt = Optional.ofNullable(Strings.emptyToNull(sb.toString()));

This works, but seems ugly and not very functional.

PS: There is a similar question but the accepted answer is wrong. Specifically, if name1 is empty and name2 is not, it returns an empty optional.

like image 376
spinlok Avatar asked Feb 16 '18 22:02

spinlok


2 Answers

One solution is to stream and reduce():

Optional<String> joinedOpt = Stream.of(name1, name2)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .reduce((a, b) -> a + " AND " + b);

Feel free to replace the filter/map combo with Java 9 or Guava as others have suggested.

like image 52
shmosel Avatar answered Sep 20 '22 06:09

shmosel


Possible solution in Java 1.9 would be

 Optional<String> name1 = Optional.of("x");
 Optional<String> name2 = Optional.of("y");
 String s = Stream.concat(name1.stream(), name2.stream()).collect(Collectors.joining(" AND "));
 System.out.println(s);

With Java 1.8 there is no way that you can go from an Optional to a Stream. However, you can add such a conversion quite easily to Java 1.8.

static <T> Stream<T> of(Optional<T> option) {
    return option.map(Stream::of).orElseGet(Stream::empty);
}

And now you can use the of method to concatenate both streams.

Now, to get an empty Optional if there is no result, you can wrap the result into an Optional and do a simple filter.

Optional<String> result = Optional.of(collect).filter(Optional::isPresent);
like image 36
Sleiman Jneidi Avatar answered Sep 21 '22 06:09

Sleiman Jneidi