The Stream.flatMap()
operation transforms a stream of
a, b, c
into a stream that contains zero or more elements for each input el
Finally I figured out that flatMap
is its own "inverse" so to say. I oversaw that flatMap
not necessarily increases the number of elements. It may also decrease the number of elements by emitting an empty stream for some of the elements. To implement a group-by operation, the function called by flatMap
needs minimal internal state, namely the most recent element. It either returns an empty stream or, at the end of a group, it returns the reduced-to group representative.
Here is a quick implementation where groupBorder
must return true
if the two elements passed in do not belong to the same group, i.e. between them is the group border. The combiner
is the group function that combines, for example (1,a), (1,a), (1,a) into (3,a), given that your group elements are, tuples (int, string).
public class GroupBy implements Function>{
private final BiPredicate groupBorder;
private final BinaryOperator combiner;
private X latest = null;
public GroupBy(BiPredicate groupBorder,
BinaryOperator combiner) {
this.groupBorder = groupBorder;
this.combiner = combiner;
}
@Override
public Stream apply(X elem) {
// TODO: add test on end marker as additonal parameter for constructor
if (elem==null) {
return latest==null ? Stream.empty() : Stream.of(latest);
}
if (latest==null) {
latest = elem;
return Stream.empty();
}
if (groupBorder.test(latest, elem)) {
Stream result = Stream.of(latest);
latest = elem;
return result;
}
latest = combiner.apply(latest, elem);
return Stream.empty();
}
}
There is one caveat though: to ship the last group of the whole stream, an end marker must be stuck as the last element into the stream. The above code assumes it is null
, but an additional end-marker-tester could be added.
I could not come up with a solution that does not rely on the end marker.
Further I did not also convert between incoming and outgoing elements. For a unique-operation, this would just work. For a count-operation, a previous step would have to map individual elements to a counting object.