Skip to content

Commit 292b48d

Browse files
committed
Add TakeWhile iterator
1 parent 23eca34 commit 292b48d

File tree

5 files changed

+169
-1
lines changed

5 files changed

+169
-1
lines changed

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ itx.Repeat2(42, "42").Take(5).Collect()
691691
> otherwise it's likely to result in an infinite while loop. Consider bounding the size of the
692692
> iterator before consuming (e.g. using [Take](#take)).
693693
694-
### Take
694+
<h3 id="take">Take & TakeWhile</h3>
695695

696696
Take limits the number of values of an iterator to a specified size. If the iterator has fewer
697697
values than the provided number then it behaves as though the original iterator is not changed.
@@ -709,6 +709,26 @@ maps.Collect(it.Take2(slices.All([]int{1, 2, 3}), 2))
709709
itx.FromSlice([]int{1, 2, 3}).Enumerate().Take(2).Collect()
710710
```
711711

712+
TakeWhile yields values from the provided iterator whilst the predicate returns true for each value.
713+
After the first value results in the predicate returning false, the iterator is exhausted.
714+
715+
```go
716+
lessThanThree := func(number int) { number < 3 }
717+
718+
slices.Collect(it.TakeWhile(slices.Values([]int{1, 2, 3}), lessThanThree))
719+
720+
// Chainable
721+
itx.FromSlice([]int{1, 2, 3}).TakeWhile(lessThanThree).Collect()
722+
723+
lessThanThreeRight := func(_ string, number int) { return number < 3 }
724+
725+
// Taking from iter.Seq2
726+
maps.Collect(it.TakeWhile2(maps.All(map[string]int{"one": 1, "four": 4}), lessThanThreeRight))
727+
728+
// As above, but chainable
729+
itx.FromMap(map[string]int{"one": 1, "four": 4}).TakeWhile(lessThanThreeRight).Collect()
730+
```
731+
712732
### Zip, Left & Right
713733

714734
`Zip` yields pairs of values from two iterators. It is exhausted when one of the two provided

it/itx/take.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,15 @@ func (iterator Iterator[V]) Take(limit uint) Iterator[V] {
1111
func (iterator Iterator2[V, W]) Take(limit uint) Iterator2[V, W] {
1212
return Iterator2[V, W](it.Take2(iterator, limit))
1313
}
14+
15+
// TakeWhile is a convenience method for chaining [it.TakeWhile] on
16+
// [Iterator]s.
17+
func (iterator Iterator[V]) TakeWhile(predicate func(V) bool) Iterator[V] {
18+
return Iterator[V](it.TakeWhile(iterator, predicate))
19+
}
20+
21+
// TakeWhile is a convenience method for chaining [it.TakeWhile2] on
22+
// [Iterator2]s.
23+
func (iterator Iterator2[V, W]) TakeWhile(predicate func(V, W) bool) Iterator2[V, W] {
24+
return Iterator2[V, W](it.TakeWhile2(iterator, predicate))
25+
}

it/itx/take_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"maps"
66

7+
"github.com/BooleanCat/go-functional/v2/it/filter"
78
"github.com/BooleanCat/go-functional/v2/it/itx"
89
)
910

@@ -24,3 +25,22 @@ func ExampleIterator2_Take() {
2425
fmt.Println(len(numbers))
2526
// Output: 2
2627
}
28+
29+
func ExampleIterator_TakeWhile() {
30+
for number := range itx.FromSlice([]int{1, 2, 3, 4, 5}).TakeWhile(filter.LessThan(4)) {
31+
fmt.Println(number)
32+
}
33+
34+
// Output:
35+
// 1
36+
// 2
37+
// 3
38+
}
39+
40+
func ExampleIterator2_TakeWhile() {
41+
lessThanFour := func(int, v int) bool { return v < 4 }
42+
43+
_, numbers := itx.FromSlice([]int{1, 2, 3, 4, 5}).Enumerate().TakeWhile(lessThanFour).Collect()
44+
fmt.Println(numbers)
45+
// Output: [1 2 3]
46+
}

it/take.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,35 @@ func Take2[V, W any](delegate func(func(V, W) bool), limit uint) iter.Seq2[V, W]
3333
}
3434
}
3535
}
36+
37+
// TakeWhile yields values from a delegate iterator until the predicate returns
38+
// false.
39+
func TakeWhile[V any](delegate func(func(V) bool), predicate func(V) bool) iter.Seq[V] {
40+
return func(yield func(V) bool) {
41+
for value := range delegate {
42+
if predicate(value) {
43+
if !yield(value) {
44+
return
45+
}
46+
} else {
47+
return
48+
}
49+
}
50+
}
51+
}
52+
53+
// TakeWhile2 yields pairs of values from a delegate iterator until the
54+
// predicate returns false.
55+
func TakeWhile2[V, W any](delegate func(func(V, W) bool), predicate func(V, W) bool) iter.Seq2[V, W] {
56+
return func(yield func(V, W) bool) {
57+
for v, w := range delegate {
58+
if predicate(v, w) {
59+
if !yield(v, w) {
60+
return
61+
}
62+
} else {
63+
return
64+
}
65+
}
66+
}
67+
}

it/take_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/BooleanCat/go-functional/v2/internal/assert"
1010
"github.com/BooleanCat/go-functional/v2/it"
11+
"github.com/BooleanCat/go-functional/v2/it/filter"
1112
)
1213

1314
func ExampleTake() {
@@ -102,3 +103,86 @@ func TestTake2YieldFalse(t *testing.T) {
102103
return false
103104
})
104105
}
106+
107+
func ExampleTakeWhile() {
108+
for number := range it.TakeWhile(slices.Values([]int{1, 2, 3, 4, 5}), filter.LessThan(4)) {
109+
fmt.Println(number)
110+
}
111+
112+
// Output:
113+
// 1
114+
// 2
115+
// 3
116+
}
117+
118+
func TestTakeWhileYieldsFalse(t *testing.T) {
119+
t.Parallel()
120+
121+
seq := it.TakeWhile(slices.Values([]int{1, 2, 3, 4, 5}), filter.LessThan(4))
122+
123+
seq(func(n int) bool {
124+
return false
125+
})
126+
}
127+
128+
func TestTakeWhileEmpty(t *testing.T) {
129+
t.Parallel()
130+
131+
assert.Empty[int](t, slices.Collect(it.TakeWhile(it.Exhausted[int](), filter.Passthrough)))
132+
}
133+
134+
func TestTakeWhileNeverTake(t *testing.T) {
135+
t.Parallel()
136+
137+
numbers := slices.Collect(it.TakeWhile(slices.Values([]int{1, 2, 3}), func(int) bool { return false }))
138+
assert.Empty[int](t, numbers)
139+
}
140+
141+
func TestTakeWhileTakeAll(t *testing.T) {
142+
t.Parallel()
143+
144+
numbers := slices.Collect(it.TakeWhile(slices.Values([]int{1, 2, 3}), filter.Passthrough))
145+
assert.SliceEqual(t, []int{1, 2, 3}, numbers)
146+
}
147+
148+
func ExampleTakeWhile2() {
149+
_, values := it.Collect2(it.TakeWhile2(slices.All([]int{1, 2, 3}), func(i int, v int) bool {
150+
return v < 3
151+
}))
152+
153+
fmt.Println(values)
154+
// Output: [1 2]
155+
}
156+
157+
func TestTakeWhile2YieldsFalse(t *testing.T) {
158+
t.Parallel()
159+
160+
seq := it.TakeWhile2(slices.All([]int{1, 2, 3}), func(i int, v int) bool {
161+
return v < 3
162+
})
163+
164+
seq(func(i int, v int) bool {
165+
return false
166+
})
167+
}
168+
169+
func TestTakeWhile2Empty(t *testing.T) {
170+
t.Parallel()
171+
172+
_, values := it.Collect2(it.TakeWhile2(it.Exhausted2[int, int](), filter.Passthrough2))
173+
assert.Empty[int](t, values)
174+
}
175+
176+
func TestTakeWhile2NeverTake(t *testing.T) {
177+
t.Parallel()
178+
179+
_, values := it.Collect2(it.TakeWhile2(slices.All([]int{1, 2, 3}), func(int, int) bool { return false }))
180+
assert.Empty[int](t, values)
181+
}
182+
183+
func TestTakeWhile2TakeAll(t *testing.T) {
184+
t.Parallel()
185+
186+
_, values := it.Collect2(it.TakeWhile2(slices.All([]int{1, 2, 3}), filter.Passthrough2))
187+
assert.SliceEqual(t, []int{1, 2, 3}, values)
188+
}

0 commit comments

Comments
 (0)