Kkkev
Kkkev

Reputation: 4925

Guava: transform an List<Optional<T>> to List<T> keeping just present values

Is there an elegant way of using Guava to transform from a list of optionals to a list of present values?

For example, going from

ImmutableList.of(
    Optional.of("Tom"), Optional.<String>absent(), Optional.of("Dick"),
    Optional.of("Harry"), Optional.<String>absent()
)

to a list containing just

["Tom", "Dick", "Harry"]

One approach would be:

List<T> filterPresent(List<Optional<T>> inputs) {
    return FluentIterable.from(inputs)
            .filter(new Predicate<Optional<T>>() {
                @Override
                public boolean apply(Optional<T> optional) {
                    return optional.isPresent();
                }
            }).transform(new Function<Optional<T>, T>() {
                @Override
                public T apply(Optional<T> optional) {
                    return optional.get();
                }
            }).toList();
}

But this is verbose.

Java 8 is not an option, unfortunately.

Upvotes: 5

Views: 2867

Answers (4)

Grzegorz Rożniecki
Grzegorz Rożniecki

Reputation: 28035

There's actualy built-in method for this in Guava: presentInstances in Optional:

Returns the value of each present instance from the supplied optionals, in order, skipping over occurrences of absent(). Iterators are unmodifiable and are evaluated lazily.

Example:

List<Optional<String>> optionalNames = ImmutableList.of(
    Optional.of("Tom"), Optional.<String>absent(), Optional.of("Dick"),
    Optional.of("Harry"), Optional.<String>absent());

Iterable<String> presentNames = Optional.presentInstances(optionalNames); // lazy

// copy to List if needed
List<String> presentNamesList = ImmutableList.copyOf(presentNames);
System.out.println(presentNamesList); // ["Tom", "Dick", "Harry"]

Upvotes: 15

Viktor Nordling
Viktor Nordling

Reputation: 9324

If you are calling a method in a loop which returns an Optional and you want to create a List of the returned values which are present, then you can use the toSet method on the Optional in conjunction with the addAll method on the List, like so:

List<String> strings = newArrayList();
for (Long id : ids) {
   strings.addAll(getString(id).toSet());
}

This is useful if you want to return a List, rather than an Iterable, which you get from Optional.presentInstances.

Upvotes: 0

Seelenvirtuose
Seelenvirtuose

Reputation: 20648

You could hide the predicate instances behind method calls to make the code more readable:

List<T> filterPresent(List<Optional<T>> inputs) {
    return FluentIterable.from(inputs).filter(present()).transform(value()).toList();
}

static <T> Predicate<Optional<T>> present() {
    return new Predicate<Optional<T>>() {
        @Override
        public boolean apply(Optional<T> optional) {
            return optional.isPresent();
        }
    };
}

static <T> Function<Optional<T>, T> value() {
    return new Function<Optional<T>, T>() {
        @Override
        public T apply(Optional<T> optional) {
            return optional.get();
        }
    };
}

Unfortunately, there is no easier way in the pre-Java-8-life.

Upvotes: 0

Konstantin Yovkov
Konstantin Yovkov

Reputation: 62874

Why not do it in the old-fashioned Java way:

List<T> result = new ArrayList<T>();
for (Optional<T> optional : inputs) {
    if (optional.isPresent()) {
        result.add(optional.get());
    }
}
return result;

Upvotes: 5

Related Questions