Skip to content

Commit

Permalink
BiCollectors.partitioningBy()
Browse files Browse the repository at this point in the history
  • Loading branch information
fluentfuture committed Sep 19, 2024
1 parent 2f63518 commit 7f11037
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 3 deletions.
77 changes: 77 additions & 0 deletions mug/src/main/java/com/google/mu/util/stream/BiCollectors.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -453,6 +454,82 @@ public static <K, V, G> BiCollector<K, V, BiStream<G, V>> groupingBy(
};
}

/**
* Returns a BiCollector that partitions the incoming pairs into two groups: elements that match
* {@code predicate}, and those that don't. Both groups are stored in a BiStream.
*
* <p>For example:
*
* <pre>{@code
* timeSeries
* .collect(partitioningBy((time, event) -> event.isImportant()))
* .andThen((importantEvents, unimportantEvents) -> ...);
* }</pre>
*
* @since 8.1
*/
public static <K, V> BiCollector<K, V, Both<BiStream<K, V>, BiStream<K, V>>> partitioningBy(
BiPredicate<? super K, ? super V> predicate) {
return partitioningBy(predicate, BiStream::toBiStream);
}

/**
* Returns a BiCollector that partitions the incoming pairs into two groups: elements that match
* {@code predicate}, and those that don't, and use {@code downstream} collector to collect the
* pairs.
*
* <p>For example:
*
* <pre>{@code
* timeSeries
* .collect(partitioningBy((time, event) -> event.isImportant(), toSortedImmutableMap()))
* .andThen((importantEvents, unimportantEvents) -> ...);
* }</pre>
*
* @param <K> the input key type
* @param <V> the input value type
* @param <R> the result type of the downstream collector
* @since 8.1
*/
public static <K, V, R> BiCollector<K, V, Both<R, R>> partitioningBy(
BiPredicate<? super K, ? super V> predicate,
BiCollector<? super K, ? super V, ? extends R> downstream) {
return partitioningBy(predicate, downstream, downstream);
}

/**
* Returns a BiCollector that partitions the incoming pairs into two groups: elements that match
* {@code predicate}, and those that don't, and use {@code ifTrue} and {@code ifFalse} downstream
* collectors respectively to collect the pairs.
*
* <p>For example:
*
* <pre>{@code
* timeSeries
* .collect(
* partitioningBy((time, event) -> event.isImportant(), toImmutableMap(), counting()))
* .andThen((importantEvents, unimportantCount) -> ...);
* }</pre>
*
* @param <K> the input key type
* @param <V> the input value type
* @param <T> the result type for the pairs that evaluate to true
* @param <F> the result type for the pairs that evaluate to false
* @since 8.1
*/
public static <K, V, T, F> BiCollector<K, V, Both<T, F>> partitioningBy(
BiPredicate<? super K, ? super V> predicate,
BiCollector<? super K, ? super V, ? extends T> ifTrue,
BiCollector<? super K, ? super V, ? extends F> ifFalse) {
requireNonNull(predicate);
return mapping(
AbstractMap.SimpleImmutableEntry<K, V>::new,
MoreCollectors.partitioningBy(
(Map.Entry<K, V> e) -> predicate.test(e.getKey(), e.getValue()),
ifTrue.collectorOf(Map.Entry::getKey, Map.Entry::getValue),
ifFalse.collectorOf(Map.Entry::getKey, Map.Entry::getValue)));
}

/**
* Returns a {@link BiCollector} that maps the result of {@code upstream} collector using
* {@code finisher}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ int arity() {
* @since 6.0
*/
public static <E, R> Collector<E, ?, Both<R, R>> partitioningBy(
Predicate<? super E> predicate, Collector<E, ?, R> downstream) {
Predicate<? super E> predicate, Collector<E, ?, ? extends R> downstream) {
return partitioningBy(predicate, downstream, downstream);
}

Expand Down Expand Up @@ -564,8 +564,8 @@ int arity() {
*/
public static <E, A1, A2, T, F> Collector<E, ?, Both<T, F>> partitioningBy(
Predicate<? super E> predicate,
Collector<E, A1, T> downstreamIfTrue,
Collector<E, A2, F> downstreamIfFalse) {
Collector<E, A1, ? extends T> downstreamIfTrue,
Collector<E, A2, ? extends F> downstreamIfFalse) {
requireNonNull(predicate);
Supplier<A1> factory1 = downstreamIfTrue.supplier();
Supplier<A2> factory2 = downstreamIfFalse.supplier();
Expand Down
18 changes: 18 additions & 0 deletions mug/src/test/java/com/google/mu/util/stream/BiCollectorsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.mu.util.stream.BiCollectors.collectingAndThen;
import static com.google.mu.util.stream.BiCollectors.counting;
import static com.google.mu.util.stream.BiCollectors.groupingBy;
import static com.google.mu.util.stream.BiCollectors.maxByKey;
import static com.google.mu.util.stream.BiCollectors.maxByValue;
import static com.google.mu.util.stream.BiCollectors.minByKey;
import static com.google.mu.util.stream.BiCollectors.minByValue;
import static com.google.mu.util.stream.BiCollectors.partitioningBy;
import static com.google.mu.util.stream.BiCollectors.toMap;
import static com.google.mu.util.stream.BiStream.biStream;
import static com.google.mu.util.stream.BiStreamTest.assertKeyValues;
Expand Down Expand Up @@ -306,6 +308,22 @@ public class BiCollectorsTest {
.inOrder();
}

@Test public void testPartitioningBy_sameDownstreamCollector() {
String result =
BiStream.of(1, "one", 2, "two", 3, "three", 4, "four", 5, "five")
.collect(partitioningBy((i, n) -> i % 2 == 1))
.andThen((odds, evens) -> "odd:" + odds.toMap() + "; even:" + evens.toMap());
assertThat(result).isEqualTo("odd:{1=one, 3=three, 5=five}; even:{2=two, 4=four}");
}

@Test public void testPartitioningBy_differentDownstreamCollectors() {
String result =
BiStream.of(1, "one", 2, "two", 3, "three", 4, "four", 5, "five")
.collect(partitioningBy((i, n) -> i % 2 == 1, toMap(), counting()))
.andThen((odds, evens) -> "odd:" + odds + "; count of even:" + evens);
assertThat(result).isEqualTo("odd:{1=one, 3=three, 5=five}; count of even:2");
}

@Test public void testMapping_downstreamCollector() {
BiStream<String, Integer> salaries = BiStream.of("Joe", 100, "Tom", 200);
assertThat(salaries.collect(BiCollectors.mapping(Joiner.on(':')::join, toList())))
Expand Down

0 comments on commit 7f11037

Please sign in to comment.