Skip to content

Commit 56ef3be

Browse files
mimol91samber
andauthored
feat: support for buffer iterator (#824)
* support for buffer iterator * Code review fix * fix: transforming the Buffer helper into a pull-based operator (it receives an iterator instead of channel) * doc(it): adding loit.Buffer to doc --------- Co-authored-by: Samuel Berthe <[email protected]>
1 parent 6a9f881 commit 56ef3be

5 files changed

Lines changed: 94 additions & 21 deletions

File tree

docs/CLAUDE.md

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Use `variantHelpers` for different versions of the **same helper function**:
8888
```yaml
8989
variantHelpers:
9090
- core#slice#map # func Map[T, R]([]T, func(T, int) R) []R
91+
- core#slice#maperr # func MapErr[T, R]([]T, func(T, int) (R, error)) ([]R, error)
9192
- core#slice#mapi # func MapI[T, R]([]T, func(T, int) R) []R (with index)
9293
- core#slice#mapwithcontext # func MapWithContext[T, R]([]T, func(T, int, context.Context) R, context.Context) []R
9394
```
@@ -194,6 +195,7 @@ Use these established subCategories:
194195
- For function variants, use consistent suffixes:
195196
- `F` suffix for function-based versions (lazy evaluation)
196197
- `I` suffix for variants having `index int` argument in predicate callback
198+
- `Err` suffix for variants returning an error in predicate callback
197199
- `WithContext` suffix when context.Context is provided
198200
- `X` suffix for helpers with varying arguments (eg: MustX: Must2, Must3, Must4...)
199201

@@ -211,27 +213,6 @@ Add these examples in the source code comments, on top of helpers, with a syntax
211213

212214
If the documentation is created at the same time of the helper source code, then the Go playground execution might fail, since we need to merge+release the source code first to make this new helper available to Go playground compiler. In that case, skip the creation of the example and set no URL.
213215

214-
## Validation Scripts
215-
216-
Run these scripts to validate your documentation:
217-
218-
```bash
219-
# Check cross-references
220-
node scripts/check-cross-references.js
221-
222-
# Check for duplicates in categories
223-
node scripts/check-duplicates-in-category.js
224-
225-
# Check filename matches frontmatter
226-
node scripts/check-filename-matches-frontmatter.js
227-
228-
# Check for similar existing helpers
229-
node scripts/check-similar-exists.js
230-
231-
# Check for similar keys in directory
232-
node scripts/check-similar-keys-exist-in-directory.js
233-
```
234-
235216
## Example: Complete File
236217

237218
```yaml

docs/data/it-buffer.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
name: Buffer
3+
slug: buffer
4+
sourceRef: it/seq.go#L1162
5+
category: it
6+
subCategory: sequence
7+
signatures:
8+
- "func Buffer[T any](seq iter.Seq[T], size int) iter.Seq[[]T]"
9+
playUrl: ""
10+
variantHelpers:
11+
- it#sequence#buffer
12+
similarHelpers:
13+
- it#sequence#chunk
14+
- it#sequence#sliding
15+
- it#sequence#window
16+
position: 65
17+
---
18+
19+
Returns a sequence of slices, each containing up to size items read from the sequence.
20+
The last slice may be smaller if the sequence closes before filling the buffer.
21+
22+
Examples:
23+
24+
```go
25+
seq := func(yield func(int) bool) {
26+
_ = yield(1)
27+
_ = yield(2)
28+
_ = yield(3)
29+
_ = yield(4)
30+
_ = yield(5)
31+
_ = yield(6)
32+
_ = yield(7)
33+
}
34+
buffers := it.Buffer(seq, 3)
35+
var result [][]int
36+
for buffer := range buffers {
37+
result = append(result, buffer)
38+
}
39+
// result contains [[1 2 3] [4 5 6] [7]]
40+
```

docs/docs/contributing.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ Functions use `~[]T` constraints to accept any slice type, including named slice
2424
## Variants
2525
When applicable, some functions can be added to sub-package as well: `mutable`, `it` and `parallel`. Add a documentation for each helper.
2626

27+
For function variants, use consistent suffixes:
28+
- `F` suffix for function-based versions (lazy evaluation)
29+
- `I` suffix for variants having `index int` argument in predicate callback
30+
- `Err` suffix for variants returning an error in predicate callback
31+
- `WithContext` suffix when context.Context is provided
32+
- `X` suffix for helpers with varying arguments (eg: MustX: Must2, Must3, Must4...)
33+
2734
## Testing
2835
We try to maintain code coverage above 90%.
2936

it/seq.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,3 +1158,27 @@ func TrimSuffix[T comparable, I ~func(func(T) bool)](collection I, suffix []T) I
11581158
}
11591159
}
11601160
}
1161+
1162+
// Buffer returns a sequence of slices, each containing up to size items read from the channel.
1163+
// The last slice may be smaller if the channel closes before filling the buffer.
1164+
func Buffer[T any](seq iter.Seq[T], size int) iter.Seq[[]T] {
1165+
return func(yield func([]T) bool) {
1166+
buffer := make([]T, 0, size)
1167+
1168+
seq(func(v T) bool {
1169+
buffer = append(buffer, v)
1170+
if len(buffer) < size {
1171+
return true // keep pulling
1172+
}
1173+
// Buffer full, yield it
1174+
result := buffer
1175+
buffer = make([]T, 0, size) // allocate new buffer
1176+
return yield(result) // false = stop, true = continue
1177+
})
1178+
1179+
// Yield remaining partial buffer
1180+
if len(buffer) > 0 {
1181+
yield(buffer)
1182+
}
1183+
}
1184+
}

it/seq_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,3 +1799,24 @@ func TestTrimSuffix(t *testing.T) {
17991799
actual = TrimSuffix(values("a", "b", "c", "d", "e", "f", "g"), []string{})
18001800
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, slices.Collect(actual))
18011801
}
1802+
1803+
func TestBuffer(t *testing.T) {
1804+
t.Parallel()
1805+
is := assert.New(t)
1806+
1807+
// full batches
1808+
batches := slices.Collect(Buffer(RangeFrom(1, 6), 2))
1809+
is.Equal([][]int{{1, 2}, {3, 4}, {5, 6}}, batches)
1810+
1811+
// partial last batch
1812+
batches2 := slices.Collect(Buffer(RangeFrom(1, 5), 2))
1813+
is.Equal([][]int{{1, 2}, {3, 4}, {5}}, batches2)
1814+
1815+
// empty channel
1816+
batches3 := slices.Collect(Buffer(RangeFrom(1, 0), 2))
1817+
is.Empty(batches3)
1818+
1819+
// stop after first batch (early termination)
1820+
batches4 := slices.Collect(Take(Buffer(RangeFrom(1, 6), 2), 1))
1821+
is.Equal([][]int{{1, 2}}, batches4)
1822+
}

0 commit comments

Comments
 (0)