diff --git a/CHANGELOG.md b/CHANGELOG.md
index f09e2f8..5dc82cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
### 0.6.0
+ Implement `dropLast(n)`
++ Add support for `zipWith(collection)` and `zipWith(iterator)`
### 0.5.0
+ Implement `reverse()`
diff --git a/README.md b/README.md
index ed8cfab..e66d9b1 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,8 @@ implementation("com.ginsberg:gatherers4j:0.6.0")
| `shuffle(rg)` | Shuffle the stream into a random order using the specified `RandomGenerator` |
| `throttle(amount, duration)` | Limit stream elements to `amount` elements over `duration`, pausing until a new `duration` period starts |
| `withIndex()` | Maps all elements of the stream as-is along with their 0-based index |
+| `zipWith(collection)` | Creates a stream of `Pair` objects whose values come from the input stream and argument collection |
+| `zipWith(iterator)` | Creates a stream of `Pair` objects whose values come from the input stream and argument iterator |
| `zipWith(stream)` | Creates a stream of `Pair` objects whose values come from the input stream and argument stream |
| `zipWithNext()` | Creates a stream of `List` objects via a sliding window of width 2 and stepping 1 |
diff --git a/src/main/java/com/ginsberg/gatherers4j/Gatherers4j.java b/src/main/java/com/ginsberg/gatherers4j/Gatherers4j.java
index 167920f..6d0dac2 100644
--- a/src/main/java/com/ginsberg/gatherers4j/Gatherers4j.java
+++ b/src/main/java/com/ginsberg/gatherers4j/Gatherers4j.java
@@ -18,6 +18,8 @@
import java.math.BigDecimal;
import java.time.Duration;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Function;
@@ -343,7 +345,25 @@ public static IndexingGatherer withIndex() {
}
/**
- * Creates a stream of `Pair` objects whose values come from the input stream and argument stream
+ * Creates a stream of `Pair` objects whose values come from the stream this is called on and the argument collection
+ *
+ * @param other A non-null collection to zip with
+ */
+ public static Gatherer> zipWith(final Collection other) {
+ return new ZipWithGatherer<>(other);
+ }
+
+ /**
+ * Creates a stream of `Pair` objects whose values come from the stream this is called on and the argument iterator
+ *
+ * @param other A non-null iterator to zip with
+ */
+ public static Gatherer> zipWith(final Iterator other) {
+ return new ZipWithGatherer<>(other);
+ }
+
+ /**
+ * Creates a stream of `Pair` objects whose values come from the stream this is called on and the argument stream
*
* @param other A non-null stream to zip with
*/
@@ -351,6 +371,7 @@ public static Gatherer> zipWith
return new ZipWithGatherer<>(other);
}
+
/**
* Creates a stream of `List` objects via a sliding window of width 2 and stepping 1
*/
diff --git a/src/main/java/com/ginsberg/gatherers4j/ZipWithGatherer.java b/src/main/java/com/ginsberg/gatherers4j/ZipWithGatherer.java
index f0f4daf..f02892b 100644
--- a/src/main/java/com/ginsberg/gatherers4j/ZipWithGatherer.java
+++ b/src/main/java/com/ginsberg/gatherers4j/ZipWithGatherer.java
@@ -16,16 +16,31 @@
package com.ginsberg.gatherers4j;
-import java.util.Objects;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.Spliterator;
import java.util.stream.Gatherer;
import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import static com.ginsberg.gatherers4j.GathererUtils.mustNotBeNull;
public class ZipWithGatherer implements Gatherer> {
private final Spliterator otherSpliterator;
+ ZipWithGatherer(final Collection other) {
+ mustNotBeNull(other, "Other collection must not be null");
+ this(other.stream());
+ }
+
+ ZipWithGatherer(final Iterator other) {
+ mustNotBeNull(other, "Other iterator must not be null");
+ final Iterable iterable = () -> other;
+ this(StreamSupport.stream(iterable.spliterator(), false));
+ }
+
ZipWithGatherer(final Stream other) {
- Objects.requireNonNull(other, "Other stream must not be null");
+ mustNotBeNull(other, "Other stream must not be null");
otherSpliterator = other.spliterator();
}
@@ -33,7 +48,7 @@ public class ZipWithGatherer implements Gatherer> integrator() {
return (_, element, downstream) -> otherSpliterator
.tryAdvance(
- it -> downstream.push(new Pair<>(element, it))
+ it -> downstream.push(new Pair<>(element, it))
) && !downstream.isRejecting();
}
}
diff --git a/src/test/java/com/ginsberg/gatherers4j/ZipWithGathererTest.java b/src/test/java/com/ginsberg/gatherers4j/ZipWithGathererTest.java
index 5eef0ed..4aa1512 100644
--- a/src/test/java/com/ginsberg/gatherers4j/ZipWithGathererTest.java
+++ b/src/test/java/com/ginsberg/gatherers4j/ZipWithGathererTest.java
@@ -18,19 +18,73 @@
import org.junit.jupiter.api.Test;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
class ZipWithGathererTest {
@Test
- void zipGatherer() {
+ void inputCollectionMustNotBeNull() {
+ assertThatThrownBy(() -> Stream.of("A")
+ .gather(Gatherers4j.zipWith((Collection)null)).toList()
+ ).isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ void inputIteratorMustNotBeNull() {
+ assertThatThrownBy(() -> Stream.of("A")
+ .gather(Gatherers4j.zipWith((Iterator)null)).toList()
+ ).isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ void inputStreamMustNotBeNull() {
+ assertThatThrownBy(() -> Stream.of("A")
+ .gather(Gatherers4j.zipWith((Stream)null)).toList()
+ ).isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ void interleavingGathererThisEmpty() {
// Arrange
- final Stream left = Stream.of("A", "B", "C");
+ final Stream left = Stream.empty();
final Stream right = Stream.of(1, 2, 3);
+ // Act
+ final List> output = left
+ .gather(Gatherers4j.zipWith(right))
+ .toList();
+
+ // Assert
+ assertThat(output).isEmpty();
+ }
+
+ @Test
+ void zipGathererOtherEmpty() {
+ // Arrange
+ final Stream left = Stream.of("A", "B", "C");
+ final Stream right = Stream.empty();
+
+ // Act
+ final List> output = left
+ .gather(Gatherers4j.zipWith(right))
+ .toList();
+
+ // Assert
+ assertThat(output).isEmpty();
+ }
+
+ @Test
+ void zipWithCollectionGatherer() {
+ // Arrange
+ final Stream left = Stream.of("A", "B", "C");
+ final Collection right = List.of(1, 2, 3);
+
// Act
final List> output = left
.gather(Gatherers4j.zipWith(right))
@@ -46,10 +100,10 @@ void zipGatherer() {
}
@Test
- void zipGathererOtherEmpty() {
+ void zipWithIteratorGatherer() {
// Arrange
final Stream left = Stream.of("A", "B", "C");
- final Stream right = Stream.empty();
+ final Iterator right = List.of(1, 2, 3).iterator();
// Act
final List> output = left
@@ -57,13 +111,18 @@ void zipGathererOtherEmpty() {
.toList();
// Assert
- assertThat(output).isEmpty();
+ assertThat(output)
+ .containsExactly(
+ new Pair<>("A", 1),
+ new Pair<>("B", 2),
+ new Pair<>("C", 3)
+ );
}
@Test
- void interleavingGathererThisEmpty() {
+ void zipWithStreamGatherer() {
// Arrange
- final Stream left = Stream.empty();
+ final Stream left = Stream.of("A", "B", "C");
final Stream right = Stream.of(1, 2, 3);
// Act
@@ -72,6 +131,11 @@ void interleavingGathererThisEmpty() {
.toList();
// Assert
- assertThat(output).isEmpty();
+ assertThat(output)
+ .containsExactly(
+ new Pair<>("A", 1),
+ new Pair<>("B", 2),
+ new Pair<>("C", 3)
+ );
}
}
\ No newline at end of file