On several occasions, I've been looking for a method that would be an inverse of Stream.flatMap. I noticed that other people do too - here's an example question on StackOverflow.
However, there are quite many ways to define it (especially the criteria for such an inverse operation). Finally I've decided to stick to the well-understood reduction semantics, and I've come up with the following two method signatures (names to be discussed) that I'd like to propose for Seq:
reducePartially works like Stream.reduce(BinaryOperator), only it yields and restarts the reduction if conditionalAccumulator returns an empty Optional:
public static Seq<T> reducePartially(BiFunction<? super T, ? super T, Optional<T>> conditionalAccumulator) {
return reducingMap(Function.identity(), conditionalAccumulator);
}
reducePartially is similar to StreamEx.collapse(BiPredicate,BinaryOperator), with the main difference being that the first argument to conditionalAccumulator is the result of previous reduction (exactly as in reduce).
reducingMap works like Stream.reduce(U, BiFunction, BinaryOperator), only it yields and restarts the reduction if conditionalAccumulator returns an empty Optional, and it has a different mechanism for providing initial Us (instead of U identity we have an initialMapper that converts the first T to the initial U):
public static <U> Seq<U> reducingMap(Function<? super T, ? extends U> initialMapper,
BiFunction<? super U, ? super T, Optional<U>> conditionalAccumulator);
reducingMap bears some resemblance to StreamEx.collapse(BiPredicate,Collector), but - again - it's much more a "reduce" than a "mark" + "merge".
Here are a few examples of how it is supposed to work:
Seq.reducePartially
Example 1: Merge words ending with hyphens with the following word:
- conditional accumulator:
(a, b) -> a.endsWith("-") ? Optional.of(withoutLastChar(a) + b) : Optional.empty();
- input:
"Sample", "hyp-", "hen-", "ated", "text"
- output:
"Sample", "hyphenated", "text"
Seq.reducingMap
Example 1: Join integers using a semicolon until 0 occurs:
- initial mapper:
String::valueOf
- conditional accumulator:
(String a, int b) -> b != 0 ? Optional.of(a + ";" + b) : Optional.empty();
- input:
7, 8, 0, 1, 5, 7, 8, 0, 3, 1, 7, 9, 0, 0, 5
- output:
"7;8", "0;1;5;7;8", "0;3;1;7;9", "0", "0;5"
I have this implemented (using a custom, lazy Spliterator) so I could provide a PR if the feature gets accepted.
On several occasions, I've been looking for a method that would be an inverse of
Stream.flatMap. I noticed that other people do too - here's an example question on StackOverflow.However, there are quite many ways to define it (especially the criteria for such an inverse operation). Finally I've decided to stick to the well-understood reduction semantics, and I've come up with the following two method signatures (names to be discussed) that I'd like to propose for
Seq:reducePartiallyworks likeStream.reduce(BinaryOperator), only it yields and restarts the reduction ifconditionalAccumulatorreturns an emptyOptional:reducePartiallyis similar toStreamEx.collapse(BiPredicate,BinaryOperator), with the main difference being that the first argument toconditionalAccumulatoris the result of previous reduction (exactly as inreduce).reducingMapworks likeStream.reduce(U, BiFunction, BinaryOperator), only it yields and restarts the reduction ifconditionalAccumulatorreturns an emptyOptional, and it has a different mechanism for providing initialUs (instead ofU identitywe have aninitialMapperthat converts the firstTto the initialU):reducingMapbears some resemblance toStreamEx.collapse(BiPredicate,Collector), but - again - it's much more a "reduce" than a "mark" + "merge".Here are a few examples of how it is supposed to work:
Seq.reducePartiallyExample 1: Merge words ending with hyphens with the following word:
(a, b) -> a.endsWith("-") ? Optional.of(withoutLastChar(a) + b) : Optional.empty();"Sample", "hyp-", "hen-", "ated", "text""Sample", "hyphenated", "text"Seq.reducingMapExample 1: Join integers using a semicolon until
0occurs:String::valueOf(String a, int b) -> b != 0 ? Optional.of(a + ";" + b) : Optional.empty();7, 8, 0, 1, 5, 7, 8, 0, 3, 1, 7, 9, 0, 0, 5"7;8", "0;1;5;7;8", "0;3;1;7;9", "0", "0;5"I have this implemented (using a custom, lazy
Spliterator) so I could provide a PR if the feature gets accepted.