Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v0.6.0 #54

Merged
merged 12 commits into from
Nov 2, 2024
Merged
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
run: ./gradlew build --no-daemon

- name: Publish
if: github.ref != 'refs/heads/main'
if: github.ref != 'refs/heads/main' && !(github.event_name == 'pull_request' && github.base_ref == 'main')
run: ./gradlew publish --no-daemon
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
### 0.6.0
+ Implement `dropLast(n)`
+ Implement `grouping()` and `groupingBy(fn)`
+ Add support for `zipWith(iterable)` and `zipWith(iterator)`
+ Add support for `interleave(iterable)` and `interleave(iterator)`
+ Add support for `appendLonger()`, `appendArgumentIfLonger()`, and `appendSourceIfLonger()` on `interleave()`
+ Add support for `argumentWhenSourceLonger()`, `sourceWhenArgumentLonger()`, `nullArgumentWhenSourceLonger()`, and `nullSourceWhenArgumentLonger` on `zipWith()`

### 0.5.0
+ Implement `reverse()`
+ Implement `maxBy(fn)` and `minBy(fn)`
Expand Down
45 changes: 41 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Add the following dependency to `pom.xml`.
<dependency>
<groupId>com.ginsberg</groupId>
<artifactId>gatherers4j</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
</dependency>
```

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

```groovy
implementation("com.ginsberg:gatherers4j:0.5.0")
implementation("com.ginsberg:gatherers4j:0.6.0")
```

# Gatherers In This Library
Expand All @@ -37,8 +37,13 @@ implementation("com.ginsberg:gatherers4j:0.5.0")
| `dedupeConsecutive()` | Remove consecutive duplicates from a stream |
| `dedupeConsecutiveBy(fn)` | Remove consecutive duplicates from a stream as returned by `fn` |
| `distinctBy(fn)` | Emit only distinct elements from the stream, as measured by `fn` |
| `dropLast(n)` | Keep all but the last `n` elements of the stream |
| `exactSize(n)` | Ensure the stream is exactly `n` elements long, or throw an `IllegalStateException` |
| `filterWithIndex(predicate)` | Filter the stream with the given `predicate`, which takes an `element` and its `index` |
| `filterWithIndex(predicate)` | Filter the stream with the given `predicate`, which takes an `element` and its `index` |
| `grouping()` | Group consecute identical elements into lists |
| `groupingBy(fn)` | Group consecutive elements that are identical according to `fn` into lists |
| `interleave(iterable)` | Creates a stream of alternating objects from the input stream and the argument iterable |
| `interleave(iterator)` | Creates a stream of alternating objects from the input stream and the argument iterator |
| `interleave(stream)` | Creates a stream of alternating objects from the input stream and the argument stream |
| `last(n)` | Constrain the stream to the last `n` values |
| `maxBy(fn)` | Return a stream containing a single element, which is the maximum value returned by the mapping function `fn` |
Expand All @@ -48,6 +53,8 @@ implementation("com.ginsberg:gatherers4j:0.5.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(iterable)` | Creates a stream of `Pair` objects whose values come from the input stream and argument iterable |
| `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 Expand Up @@ -139,6 +146,16 @@ Stream
// [Person("Todd", "Ginsberg"), Person("Emma", "Ginsberg")]
```

#### Keep all but the last `n` elements

```java
Stream.of("A", "B", "C", "D", "E")
.gather(Gatherers4j.dropLast(2))
.toList();

// ["A", "B", "C"]
```

#### Ensure the stream is exactly `n` elements long

```java
Expand All @@ -162,6 +179,27 @@ Stream.of("A", "B", "C", "D")
// ["A", "C", "D"]
```

### Group identical elements

```java
Stream.of("A", "A", "B", "B", "B", "C")
.gather(Gatherers4j.grouping())
.toList();

// [["A", "A"], ["B", "B", "B"], ["C"]]
```

### Group identical elements as measured by a function

```java
Stream.of("A", "B", "AA", "BB", "CC", "DDD")
.gather(Gatherers4j.groupingBy(String::length))
.toList();

// [["A", "B"], ["AA", "BB", "CC"], ["DDD"]]
```


#### Interleave streams of the same type into one stream

```java
Expand Down Expand Up @@ -316,7 +354,6 @@ are treated as zeros, and the calculated average is returned along with the orig
```java
someStreamOfBigDecimal()
.gather(Gatherers4j
.averageBigDecimals()
.simpleMovingAverage(10)
.includePartialValues()
.withMathContext(MathContext.DECIMAL32)
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.0
0.6.0
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dependencies {
because("Starting in Gradle 9.0, this needs to be an explicitly declared dependency")
}

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

testImplementation("org.assertj:assertj-core:3.26.3") {
because("These assertions are clearer than JUnit+Hamcrest")
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
24 changes: 8 additions & 16 deletions src/main/java/com/ginsberg/gatherers4j/BigDecimalGatherer.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,37 +48,29 @@ public Integrator<BigDecimalGatherer.State, INPUT, BigDecimal> integrator() {
};
}

/**
* When encountering a <code>null</code> value in a stream, treat it as `BigDecimal.ZERO` instead.
*/
/// When encountering a `null` value in a stream, treat it as `BigDecimal.ZERO` instead.
public BigDecimalGatherer<INPUT> treatNullAsZero() {
return treatNullAs(BigDecimal.ZERO);
}

/**
* When encountering a <code>null</code> value in a stream, treat it as the given `replacement` value instead.
*
* @param replacement The value to replace null with
*/
/// When encountering a `null` value in a stream, treat it as the given `replacement` value instead.
///
/// @param replacement The value to replace `null` with
public BigDecimalGatherer<INPUT> treatNullAs(final BigDecimal replacement) {
this.nullReplacement = replacement;
return this;
}

/**
* Replace the <code>MathContext</code> used for all mathematical operations in this class.
*
* @param mathContext A non-null <code>MathContext</code>
*/
/// Replace the `MathContext` used for all mathematical operations in this class.
///
/// @param mathContext A non-null `MathContext`
public BigDecimalGatherer<INPUT> withMathContext(final MathContext mathContext) {
mustNotBeNull(mathContext, "MathContext must not be null");
this.mathContext = mathContext;
return this;
}

/**
* Include the original input value from the stream in addition to the calculated average.
*/
/// Include the original input value from the stream in addition to the calculated average.
public WithOriginalGatherer<INPUT, BigDecimalGatherer.State, BigDecimal> withOriginal() {
return new WithOriginalGatherer<>(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ public Supplier<BigDecimalGatherer.State> initializer() {
return State::new;
}

/**
* When encountering a <code>null</code> value in a stream, treat it as `BigDecimal.ZERO` instead.
*/
/// When encountering a `null` value in a stream, treat it as `BigDecimal.ONE` instead.
public BigDecimalGatherer<INPUT> treatNullAsOne() {
return treatNullAs(BigDecimal.ONE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

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

private int windowSize;
private final int windowSize;
private boolean includePartialValues;

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

/**
* When creating a moving average and the full size of the window has not yet been reached, the
* gatherer should emit averages for what it has.
* For example, if the trailing average is over 10 values, but the stream has only emitted two
* values, the gatherer should calculate the two values and emit the answer. The default is to not
* emit anything until the full size of the window has been seen.
*/
/// When creating a moving average and the full size of the window has not yet been reached, the
/// gatherer should emit averages for what it has.
///
/// For example, if the trailing average is over 10 values, but the stream has only emitted two
/// values, the gatherer should calculate the two values and emit the answer. The default is to not
/// emit anything until the full size of the window has been seen.
public BigDecimalSimpleMovingAverageGatherer<INPUT> includePartialValues() {
includePartialValues = true;
return this;
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/com/ginsberg/gatherers4j/DropLastGatherer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2024 Todd Ginsberg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.ginsberg.gatherers4j;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Gatherer;

public class DropLastGatherer<INPUT> implements Gatherer<INPUT, DropLastGatherer.State<INPUT>, INPUT> {

private final int count;

DropLastGatherer(final int count) {
if (count <= 0) {
throw new IllegalArgumentException("DropLast count must be positive");
}
this.count = count;
}

@Override
public Supplier<State<INPUT>> initializer() {
return State::new;
}

@Override
public Integrator<State<INPUT>, INPUT, INPUT> integrator() {
return (state, element, downstream) -> {
state.elements.add(element);
return !downstream.isRejecting();
};
}

@Override
public BiConsumer<State<INPUT>, Downstream<? super INPUT>> finisher() {
return (inputState, downstream) -> {
for (int i = 0; i < inputState.elements.size() - count && !downstream.isRejecting(); i++) {
downstream.push(inputState.elements.get(i));
}
};
}

public static class State<INPUT> {
final List<INPUT> elements = new ArrayList<>();
}
}
Loading
Loading