≡ Menu

Combining two functions into one

Every now and then, you may want to apply one function to only some of the elements in your stream.

This is not so much an example of manipulating streams but rather of combining two functions into one with a predicated dictating which function to apply. In concrete terms, we will create a stream of the integers in the range 0 through 10 (non-inclusive), multiply the even numbers by two and add one, then finally display the result.

The first function, or IntUnaryOperator – as it takes and returns one int – (slightly contrived use of andThen and compose) – looks like this:

IntUnaryOperator f = IntUnaryOperator.identity()
   .andThen(i -> i + 1)
   .compose(i -> 2 * i);

The second function is just the identity:

IntUnaryOperator g = IntUnaryOperator.identity();

The predicate, or just a function taking an int and returning a boolean, was to do something for the even integers:

IntPredicate p = (i -> i % 2 == 0);

One way of combining the two functions is close to the lines of “if the value is this, do that to the value and return it, else do something else to the value and return it:”

IntUnaryOperator m = (i -> (p.test(i) 
   ? f.applyAsInt(i) 
   : g.applyAsInt(i)));

So now we can map the integers by

IntStream.range(0, 10)
   .map(m)
   .forEach(System.out::println);

However, instead of creating a function taking and returning an integer we could create a function taking an integer and returning a function (which then take and return an integer):

IntFunction<IntUnaryOperator> mm = (i -> p.test(i) ? f : g);

This looks a bit nicer, but applying it to the stream gets messy:

IntStream.range(0, 10)
    .map(i -> mm.apply(i).applyAsInt(i))
    .forEach(System.out::println);

It might make be more sensible to just skip the intermediate step and write:

IntStream.range(0, 10)
    .map(i -> (p.test(i) ? f : g)
    .applyAsInt(i))
 .forEach(System.out::println);

.. which is also a way to get around the problem of prepending or appending the functions f or g:

IntStream.range(0, 10)
    .map(i -> (p.test(i) ? f : g.andThen(j -> j + 100))
       .applyAsInt(i))
 .forEach(System.out::println);

Caveat lector: There are probably much cleaner and more sensible ways to do this. Feel free to suggest alternatives!

Comments on this entry are closed.