Jean-Luc Delarbre
Jean-Luc Delarbre

Reputation: 21

Peek data at different stages of a Java stream (or compose functions)

Let say, I have the following elements:

class A {
    String s;

    String getS() {return s;}

    static boolean aFilter(String in) {return true or false}

    static A aTransformation(A a) {return another A}
}

I would like to do the following with Java stream:

List<A> myAs = provideTheList();

myAs.stream()
    .map(A::getS)
    .filter(A::aFilter)
    .map("back to stream of A")
    .map(A::aTransformation)
    .toList();

I know I can do it this way:

myAs.stream()
    .filter(a -> A.aFilter(a.getS()))
    .map(A::aTransformation)
    .toList();

But it breaks, the expression of code as a series of transformations. I want to avoid the "a -> ...". I could write a function to do the "A.aFilter(a.getS())", but the above pattern is quite common and it's cumbersome to write additional function just for this. Something like:

myAs.stream()
    .filter(A::aFilter ° A::getS) // function composition
    ...

would be perfect, but I don't know if we can express function composition in Java.

In Java we have predicates which are composable with and, or.

I do not found a way to compose functions in stream.

Another way would be to do the "back to stream of A", I do not believe it is possible to retrieve data at "another point of the stream".

Have you an elegant way to do the operation described above ?

Upvotes: 1

Views: 96

Answers (2)

Bohemian
Bohemian

Reputation: 425278

Add a method to A:

public boolean passes() {
    return aFilter(getS());
}

Then

myAs.stream()
    .map(A::passes)
    .map(A::aTransformation)
    .toList();

Upvotes: 0

talex
talex

Reputation: 20544

You can create it yourself

static <T, U> Predicate<T> combine(Function<T, U> f, Predicate<U> p) {
    return (T t) -> p.test(f.apply(t));
}

And use it as

Stream.of(X.builder().build())
    .filter(combine(X::getS, String::isBlank))
    .toList();

PS: You can do stuff like this

Stream.of(X.builder().build())
    .filter(predicate(
        For.<X>type()
            .f(X::getS)
            .andThen(Object::toString)
            .andThen(String::isBlank)))
    .toList();

If you put little more effort in utilities you creating. Here is code:

public class For<U> {
    private static For INSTANCE = new For();

    public static <U> For<U> type() { return INSTANCE; }

    public static <T> Predicate<T> predicate(Function<T, Boolean> f) {
        return f::apply;
    }

    public <R> Function<U, R> f(Function<U, R> f) { return f; }
}

EDIT If you restricted by using pure JDK try this:

Stream.of(
        X.builder().build()
).filter(Function
        .<X>identity()
        .andThen(X::getS)
        .andThen(Object::toString)
        .andThen(String::isBlank)
        ::apply
).toList();

Upvotes: 1

Related Questions