Skip to content

Commit

Permalink
Add support for zipWith(collection) and zipWith(iterator) (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
tginsberg authored Oct 20, 2024
1 parent 96f6ba6 commit 3dd7d89
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### 0.6.0
+ Implement `dropLast(n)`
+ Add support for `zipWith(collection)` and `zipWith(iterator)`

### 0.5.0
+ Implement `reverse()`
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down
23 changes: 22 additions & 1 deletion src/main/java/com/ginsberg/gatherers4j/Gatherers4j.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -343,14 +345,33 @@ public static <INPUT> IndexingGatherer<INPUT> 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 <FIRST, SECOND> Gatherer<FIRST, Void, Pair<FIRST, SECOND>> zipWith(final Collection<SECOND> 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 <FIRST, SECOND> Gatherer<FIRST, Void, Pair<FIRST, SECOND>> zipWith(final Iterator<SECOND> 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
*/
public static <FIRST, SECOND> Gatherer<FIRST, Void, Pair<FIRST, SECOND>> zipWith(final Stream<SECOND> other) {
return new ZipWithGatherer<>(other);
}


/**
* Creates a stream of `List` objects via a sliding window of width 2 and stepping 1
*/
Expand Down
21 changes: 18 additions & 3 deletions src/main/java/com/ginsberg/gatherers4j/ZipWithGatherer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,39 @@

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<FIRST, SECOND> implements Gatherer<FIRST, Void, Pair<FIRST, SECOND>> {
private final Spliterator<SECOND> otherSpliterator;

ZipWithGatherer(final Collection<SECOND> other) {
mustNotBeNull(other, "Other collection must not be null");
this(other.stream());
}

ZipWithGatherer(final Iterator<SECOND> other) {
mustNotBeNull(other, "Other iterator must not be null");
final Iterable<SECOND> iterable = () -> other;
this(StreamSupport.stream(iterable.spliterator(), false));
}

ZipWithGatherer(final Stream<SECOND> other) {
Objects.requireNonNull(other, "Other stream must not be null");
mustNotBeNull(other, "Other stream must not be null");
otherSpliterator = other.spliterator();
}

@Override
public Integrator<Void, FIRST, Pair<FIRST, SECOND>> integrator() {
return (_, element, downstream) -> otherSpliterator
.tryAdvance(
it -> downstream.push(new Pair<>(element, it))
it -> downstream.push(new Pair<>(element, it))
) && !downstream.isRejecting();
}
}
80 changes: 72 additions & 8 deletions src/test/java/com/ginsberg/gatherers4j/ZipWithGathererTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>)null)).toList()
).isInstanceOf(IllegalArgumentException.class);
}

@Test
void inputIteratorMustNotBeNull() {
assertThatThrownBy(() -> Stream.of("A")
.gather(Gatherers4j.zipWith((Iterator<String>)null)).toList()
).isInstanceOf(IllegalArgumentException.class);
}

@Test
void inputStreamMustNotBeNull() {
assertThatThrownBy(() -> Stream.of("A")
.gather(Gatherers4j.zipWith((Stream<String>)null)).toList()
).isInstanceOf(IllegalArgumentException.class);
}

@Test
void interleavingGathererThisEmpty() {
// Arrange
final Stream<String> left = Stream.of("A", "B", "C");
final Stream<String> left = Stream.empty();
final Stream<Integer> right = Stream.of(1, 2, 3);

// Act
final List<Pair<String, Integer>> output = left
.gather(Gatherers4j.zipWith(right))
.toList();

// Assert
assertThat(output).isEmpty();
}

@Test
void zipGathererOtherEmpty() {
// Arrange
final Stream<String> left = Stream.of("A", "B", "C");
final Stream<Integer> right = Stream.empty();

// Act
final List<Pair<String, Integer>> output = left
.gather(Gatherers4j.zipWith(right))
.toList();

// Assert
assertThat(output).isEmpty();
}

@Test
void zipWithCollectionGatherer() {
// Arrange
final Stream<String> left = Stream.of("A", "B", "C");
final Collection<Integer> right = List.of(1, 2, 3);

// Act
final List<Pair<String, Integer>> output = left
.gather(Gatherers4j.zipWith(right))
Expand All @@ -46,24 +100,29 @@ void zipGatherer() {
}

@Test
void zipGathererOtherEmpty() {
void zipWithIteratorGatherer() {
// Arrange
final Stream<String> left = Stream.of("A", "B", "C");
final Stream<Integer> right = Stream.empty();
final Iterator<Integer> right = List.of(1, 2, 3).iterator();

// Act
final List<Pair<String, Integer>> output = left
.gather(Gatherers4j.zipWith(right))
.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<String> left = Stream.empty();
final Stream<String> left = Stream.of("A", "B", "C");
final Stream<Integer> right = Stream.of(1, 2, 3);

// Act
Expand All @@ -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)
);
}
}

0 comments on commit 3dd7d89

Please sign in to comment.