Skip to content

Commit 5b46f28

Browse files
authored
Release v0.6.0 (#54)
* Upgrade dependencies (#42) + Move from Gradle 8.10.1 to 8.10.2 + Move from JUnit 5.11.0 to 5.11.2 + Implement `dropLast(n)` (#44) + Add support for `zipWith(collection)` and `zipWith(iterator)` (#45) + Add support for `interleave(iterable)` and `interleave(iterator)` (#46) + Add support for appending the longer stream of an interleave operation (#47) + Add `appendLonger()` to cover when source and argument provide different number of elements + Add `appendArgumentIfLonger()` to cover when argument is longer than source + Add `appendSourceIfLonger()` to cover when source is longer than argument + Implement grouping() and groupingBy(fn) (#50) + Minor fix for averaging example in README + Switch to Markdown for JavaDoc (#51) + Misc fixes to descriptions + #48: Account for stream and argument not being the same length (#52) + I am not wild about having to use a type witness, but I'm fine with adding it as I think this use case is pretty niche and anybody who needs it could probably tolerate a type witness.
1 parent 0416276 commit 5b46f28

18 files changed

+988
-242
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
run: ./gradlew build --no-daemon
2626

2727
- name: Publish
28-
if: github.ref != 'refs/heads/main'
28+
if: github.ref != 'refs/heads/main' && !(github.event_name == 'pull_request' && github.base_ref == 'main')
2929
run: ./gradlew publish --no-daemon
3030
env:
3131
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
### 0.6.0
2+
+ Implement `dropLast(n)`
3+
+ Implement `grouping()` and `groupingBy(fn)`
4+
+ Add support for `zipWith(iterable)` and `zipWith(iterator)`
5+
+ Add support for `interleave(iterable)` and `interleave(iterator)`
6+
+ Add support for `appendLonger()`, `appendArgumentIfLonger()`, and `appendSourceIfLonger()` on `interleave()`
7+
+ Add support for `argumentWhenSourceLonger()`, `sourceWhenArgumentLonger()`, `nullArgumentWhenSourceLonger()`, and `nullSourceWhenArgumentLonger` on `zipWith()`
8+
19
### 0.5.0
210
+ Implement `reverse()`
311
+ Implement `maxBy(fn)` and `minBy(fn)`

README.md

+41-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Add the following dependency to `pom.xml`.
1515
<dependency>
1616
<groupId>com.ginsberg</groupId>
1717
<artifactId>gatherers4j</artifactId>
18-
<version>0.5.0</version>
18+
<version>0.6.0</version>
1919
</dependency>
2020
```
2121

@@ -24,7 +24,7 @@ Add the following dependency to `pom.xml`.
2424
Add the following dependency to `build.gradle` or `build.gradle.kts`
2525

2626
```groovy
27-
implementation("com.ginsberg:gatherers4j:0.5.0")
27+
implementation("com.ginsberg:gatherers4j:0.6.0")
2828
```
2929

3030
# Gatherers In This Library
@@ -37,8 +37,13 @@ implementation("com.ginsberg:gatherers4j:0.5.0")
3737
| `dedupeConsecutive()` | Remove consecutive duplicates from a stream |
3838
| `dedupeConsecutiveBy(fn)` | Remove consecutive duplicates from a stream as returned by `fn` |
3939
| `distinctBy(fn)` | Emit only distinct elements from the stream, as measured by `fn` |
40+
| `dropLast(n)` | Keep all but the last `n` elements of the stream |
4041
| `exactSize(n)` | Ensure the stream is exactly `n` elements long, or throw an `IllegalStateException` |
41-
| `filterWithIndex(predicate)` | Filter the stream with the given `predicate`, which takes an `element` and its `index` |
42+
| `filterWithIndex(predicate)` | Filter the stream with the given `predicate`, which takes an `element` and its `index` |
43+
| `grouping()` | Group consecute identical elements into lists |
44+
| `groupingBy(fn)` | Group consecutive elements that are identical according to `fn` into lists |
45+
| `interleave(iterable)` | Creates a stream of alternating objects from the input stream and the argument iterable |
46+
| `interleave(iterator)` | Creates a stream of alternating objects from the input stream and the argument iterator |
4247
| `interleave(stream)` | Creates a stream of alternating objects from the input stream and the argument stream |
4348
| `last(n)` | Constrain the stream to the last `n` values |
4449
| `maxBy(fn)` | Return a stream containing a single element, which is the maximum value returned by the mapping function `fn` |
@@ -48,6 +53,8 @@ implementation("com.ginsberg:gatherers4j:0.5.0")
4853
| `shuffle(rg)` | Shuffle the stream into a random order using the specified `RandomGenerator` |
4954
| `throttle(amount, duration)` | Limit stream elements to `amount` elements over `duration`, pausing until a new `duration` period starts |
5055
| `withIndex()` | Maps all elements of the stream as-is along with their 0-based index |
56+
| `zipWith(iterable)` | Creates a stream of `Pair` objects whose values come from the input stream and argument iterable |
57+
| `zipWith(iterator)` | Creates a stream of `Pair` objects whose values come from the input stream and argument iterator |
5158
| `zipWith(stream)` | Creates a stream of `Pair` objects whose values come from the input stream and argument stream |
5259
| `zipWithNext()` | Creates a stream of `List` objects via a sliding window of width 2 and stepping 1 |
5360

@@ -139,6 +146,16 @@ Stream
139146
// [Person("Todd", "Ginsberg"), Person("Emma", "Ginsberg")]
140147
```
141148

149+
#### Keep all but the last `n` elements
150+
151+
```java
152+
Stream.of("A", "B", "C", "D", "E")
153+
.gather(Gatherers4j.dropLast(2))
154+
.toList();
155+
156+
// ["A", "B", "C"]
157+
```
158+
142159
#### Ensure the stream is exactly `n` elements long
143160

144161
```java
@@ -162,6 +179,27 @@ Stream.of("A", "B", "C", "D")
162179
// ["A", "C", "D"]
163180
```
164181

182+
### Group identical elements
183+
184+
```java
185+
Stream.of("A", "A", "B", "B", "B", "C")
186+
.gather(Gatherers4j.grouping())
187+
.toList();
188+
189+
// [["A", "A"], ["B", "B", "B"], ["C"]]
190+
```
191+
192+
### Group identical elements as measured by a function
193+
194+
```java
195+
Stream.of("A", "B", "AA", "BB", "CC", "DDD")
196+
.gather(Gatherers4j.groupingBy(String::length))
197+
.toList();
198+
199+
// [["A", "B"], ["AA", "BB", "CC"], ["DDD"]]
200+
```
201+
202+
165203
#### Interleave streams of the same type into one stream
166204

167205
```java
@@ -316,7 +354,6 @@ are treated as zeros, and the calculated average is returned along with the orig
316354
```java
317355
someStreamOfBigDecimal()
318356
.gather(Gatherers4j
319-
.averageBigDecimals()
320357
.simpleMovingAverage(10)
321358
.includePartialValues()
322359
.withMathContext(MathContext.DECIMAL32)

VERSION.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.5.0
1+
0.6.0

build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ dependencies {
3636
because("Starting in Gradle 9.0, this needs to be an explicitly declared dependency")
3737
}
3838

39-
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
39+
testImplementation("org.junit.jupiter:junit-jupiter:5.11.2")
4040

4141
testImplementation("org.assertj:assertj-core:3.26.3") {
4242
because("These assertions are clearer than JUnit+Hamcrest")

gradle/wrapper/gradle-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

src/main/java/com/ginsberg/gatherers4j/BigDecimalGatherer.java

+8-16
Original file line numberDiff line numberDiff line change
@@ -48,37 +48,29 @@ public Integrator<BigDecimalGatherer.State, INPUT, BigDecimal> integrator() {
4848
};
4949
}
5050

51-
/**
52-
* When encountering a <code>null</code> value in a stream, treat it as `BigDecimal.ZERO` instead.
53-
*/
51+
/// When encountering a `null` value in a stream, treat it as `BigDecimal.ZERO` instead.
5452
public BigDecimalGatherer<INPUT> treatNullAsZero() {
5553
return treatNullAs(BigDecimal.ZERO);
5654
}
5755

58-
/**
59-
* When encountering a <code>null</code> value in a stream, treat it as the given `replacement` value instead.
60-
*
61-
* @param replacement The value to replace null with
62-
*/
56+
/// When encountering a `null` value in a stream, treat it as the given `replacement` value instead.
57+
///
58+
/// @param replacement The value to replace `null` with
6359
public BigDecimalGatherer<INPUT> treatNullAs(final BigDecimal replacement) {
6460
this.nullReplacement = replacement;
6561
return this;
6662
}
6763

68-
/**
69-
* Replace the <code>MathContext</code> used for all mathematical operations in this class.
70-
*
71-
* @param mathContext A non-null <code>MathContext</code>
72-
*/
64+
/// Replace the `MathContext` used for all mathematical operations in this class.
65+
///
66+
/// @param mathContext A non-null `MathContext`
7367
public BigDecimalGatherer<INPUT> withMathContext(final MathContext mathContext) {
7468
mustNotBeNull(mathContext, "MathContext must not be null");
7569
this.mathContext = mathContext;
7670
return this;
7771
}
7872

79-
/**
80-
* Include the original input value from the stream in addition to the calculated average.
81-
*/
73+
/// Include the original input value from the stream in addition to the calculated average.
8274
public WithOriginalGatherer<INPUT, BigDecimalGatherer.State, BigDecimal> withOriginal() {
8375
return new WithOriginalGatherer<>(this);
8476
}

src/main/java/com/ginsberg/gatherers4j/BigDecimalProductGatherer.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ public Supplier<BigDecimalGatherer.State> initializer() {
3232
return State::new;
3333
}
3434

35-
/**
36-
* When encountering a <code>null</code> value in a stream, treat it as `BigDecimal.ZERO` instead.
37-
*/
35+
/// When encountering a `null` value in a stream, treat it as `BigDecimal.ONE` instead.
3836
public BigDecimalGatherer<INPUT> treatNullAsOne() {
3937
return treatNullAs(BigDecimal.ONE);
4038
}

src/main/java/com/ginsberg/gatherers4j/BigDecimalSimpleMovingAverageGatherer.java

+7-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
public final class BigDecimalSimpleMovingAverageGatherer<INPUT> extends BigDecimalGatherer<INPUT> {
2626

27-
private int windowSize;
27+
private final int windowSize;
2828
private boolean includePartialValues;
2929

3030
BigDecimalSimpleMovingAverageGatherer(
@@ -43,13 +43,12 @@ public Supplier<BigDecimalGatherer.State> initializer() {
4343
return () -> new State(windowSize, includePartialValues);
4444
}
4545

46-
/**
47-
* When creating a moving average and the full size of the window has not yet been reached, the
48-
* gatherer should emit averages for what it has.
49-
* For example, if the trailing average is over 10 values, but the stream has only emitted two
50-
* values, the gatherer should calculate the two values and emit the answer. The default is to not
51-
* emit anything until the full size of the window has been seen.
52-
*/
46+
/// When creating a moving average and the full size of the window has not yet been reached, the
47+
/// gatherer should emit averages for what it has.
48+
///
49+
/// For example, if the trailing average is over 10 values, but the stream has only emitted two
50+
/// values, the gatherer should calculate the two values and emit the answer. The default is to not
51+
/// emit anything until the full size of the window has been seen.
5352
public BigDecimalSimpleMovingAverageGatherer<INPUT> includePartialValues() {
5453
includePartialValues = true;
5554
return this;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2024 Todd Ginsberg
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.ginsberg.gatherers4j;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.function.BiConsumer;
22+
import java.util.function.Supplier;
23+
import java.util.stream.Gatherer;
24+
25+
public class DropLastGatherer<INPUT> implements Gatherer<INPUT, DropLastGatherer.State<INPUT>, INPUT> {
26+
27+
private final int count;
28+
29+
DropLastGatherer(final int count) {
30+
if (count <= 0) {
31+
throw new IllegalArgumentException("DropLast count must be positive");
32+
}
33+
this.count = count;
34+
}
35+
36+
@Override
37+
public Supplier<State<INPUT>> initializer() {
38+
return State::new;
39+
}
40+
41+
@Override
42+
public Integrator<State<INPUT>, INPUT, INPUT> integrator() {
43+
return (state, element, downstream) -> {
44+
state.elements.add(element);
45+
return !downstream.isRejecting();
46+
};
47+
}
48+
49+
@Override
50+
public BiConsumer<State<INPUT>, Downstream<? super INPUT>> finisher() {
51+
return (inputState, downstream) -> {
52+
for (int i = 0; i < inputState.elements.size() - count && !downstream.isRejecting(); i++) {
53+
downstream.push(inputState.elements.get(i));
54+
}
55+
};
56+
}
57+
58+
public static class State<INPUT> {
59+
final List<INPUT> elements = new ArrayList<>();
60+
}
61+
}

0 commit comments

Comments
 (0)