amaembo / streamex

Enhancing Java Stream API
Apache License 2.0
2.18k stars 249 forks source link

Implement `dropBefore` and `dropAfter` #277

Open delanym opened 2 months ago

delanym commented 2 months ago

Requirement: Return the matching item from a stream (via short circuit) or the last item.

This requirement is deceptively simple. I have been unable to implement it using streams alone (I'm forced to start with a collection)

var aa = List.of("P", "Q", "R", "S", "T");
var bb = aa.stream()
    .filter(v -> Objects.equals(v, "S"))
    .reduce(aa.getLast(), (a, b) -> b);
System.out.printf("Result: %s%n", bb);

The result is "S". When the "S" is removed from the list the result is "T".

Proposal

A new dropBefore method (alternatively takeFrom or even takeAfterInclusive) could provide a stream having removed all items before but not including the matching item.

In above example, matching on "S" would result in Stream.of("S", "T") and no match would result in Stream.of("P", "Q", "R", "S", "T")

This would effectively be the same as dropWhile except without this characteristic If the predicate is true for all stream elements, an empty stream is returned.

This at least could provide an inclusive stream, and one could return "the matching item from a stream (via short circuit) or the FIRST item":

var aa = Stream.of("P", "Q", "R", "S", "T")
    .dropBefore(v -> Objects.equals(v, "S"))
    .reduce((a, b) -> a);

The result would be "S". When the "S" is removed from the list the result would be "P".

Then a new dropAfter method (alternatively takeUntil or takeBeforeInclusive) could provide a stream having removed all items after but not including the matching item.

var aa = Stream.of("P", "Q", "R", "S", "T")
    .dropAfter(v -> Objects.equals(v, "S"))
    .reduce((a, b) -> b);

The result would be "S". When the "S" is removed from the list the result would be "T".

The main distinction between these 2 new "before/after" methods and takeWhile and dropWhile is that the current item considered in the predicate is always part of the resulting stream whether the predicate is true or false.

This happens to be true for takeWhileInclusive. However, when the predicate is false the non-matching items are dropped (i.e. the last item, which I need for the requirement). If there was a dropWhileInclusive method the non-matching items would also be dropped (i.e. the first item).