Skip to content

Commit 040e869

Browse files
committed
Add DropWhile iterator
1 parent 292b48d commit 040e869

File tree

5 files changed

+177
-8
lines changed

5 files changed

+177
-8
lines changed

README.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ numbers := itx.FromMap(map[int]string{1: "one"}).Cycle().Take(5)
381381
> otherwise it's likely to result in an infinite while loop. Consider bounding the size of the
382382
> iterator before consuming (e.g. using [Take](#take)).
383383
384-
### Drop
384+
<h3 id="drop">Drop & DropWhile</h3>
385385

386386
Drop yields all values from a delegate iterator after dropping a number of values from the
387387
beginning. Values are not dropped immediately, but when consumption begins.
@@ -402,6 +402,24 @@ numbers := it.Drop2(maps.All(map[int]string{1: "one", 2: "two", 3: "three"}), 1)
402402
numbers := itx.FromMap(map[int]string{1: "one", 2: "two", 3: "three"}).Drop(1)
403403
```
404404

405+
DropWhile drops values from the provided iterator whilst the predicate returns true for each value.
406+
After the first value results in the predicate returning false, the iterator resumes as normal.
407+
408+
```go
409+
slices.Collect(it.DropWhile(slices.Values([]int{1, 2, 3, 4, 5}), filter.LessThan(3)))
410+
411+
// Chainable
412+
itx.FromSlice([]int{1, 2, 3, 4, 5}).DropWhile(filter.LessThan(3)).Collect()
413+
414+
lessThanThree := func(string, number int) { return number < 3 }
415+
416+
// Taking from iter.Seq2
417+
maps.Collect(it.DropWhile2(maps.All(map[string]int{"one": 1, "four": 4}), lessThanThree))
418+
419+
// As above, but chainable
420+
itx.FromMap(map[string]int{"one": 1, "four": 4}).DropWhile(lessThanThree).Collect()
421+
```
422+
405423
### Enumerate
406424

407425
Enumerating an [iter.Seq](https://pkg.go.dev/iter#Seq) like iterator returns an
@@ -713,20 +731,18 @@ TakeWhile yields values from the provided iterator whilst the predicate returns
713731
After the first value results in the predicate returning false, the iterator is exhausted.
714732

715733
```go
716-
lessThanThree := func(number int) { number < 3 }
717-
718-
slices.Collect(it.TakeWhile(slices.Values([]int{1, 2, 3}), lessThanThree))
734+
slices.Collect(it.TakeWhile(slices.Values([]int{1, 2, 3}), filter.LessThan(3)))
719735

720736
// Chainable
721-
itx.FromSlice([]int{1, 2, 3}).TakeWhile(lessThanThree).Collect()
737+
itx.FromSlice([]int{1, 2, 3}).TakeWhile(filter.LessThan(3)).Collect()
722738

723-
lessThanThreeRight := func(_ string, number int) { return number < 3 }
739+
lessThanThree := func(string, number int) { return number < 3 }
724740

725741
// Taking from iter.Seq2
726-
maps.Collect(it.TakeWhile2(maps.All(map[string]int{"one": 1, "four": 4}), lessThanThreeRight))
742+
maps.Collect(it.TakeWhile2(maps.All(map[string]int{"one": 1, "four": 4}), lessThanThree))
727743

728744
// As above, but chainable
729-
itx.FromMap(map[string]int{"one": 1, "four": 4}).TakeWhile(lessThanThreeRight).Collect()
745+
itx.FromMap(map[string]int{"one": 1, "four": 4}).TakeWhile(lessThanThree).Collect()
730746
```
731747

732748
### Zip, Left & Right

it/drop.go

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

it/drop_test.go

Lines changed: 85 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 ExampleDrop() {
@@ -79,3 +80,87 @@ func TestDrop2YieldFalse(t *testing.T) {
7980
return false
8081
})
8182
}
83+
84+
func ExampleDropWhile() {
85+
for value := range it.DropWhile(slices.Values([]int{1, 2, 3, 4, 1}), filter.LessThan(3)) {
86+
fmt.Println(value)
87+
}
88+
89+
// Output:
90+
// 3
91+
// 4
92+
// 1
93+
}
94+
95+
func TestDropWhileYieldFalse(t *testing.T) {
96+
t.Parallel()
97+
98+
numbers := it.DropWhile(slices.Values([]int{1, 2, 3, 4, 1}), filter.LessThan(3))
99+
100+
numbers(func(v int) bool {
101+
return false
102+
})
103+
}
104+
105+
func TestDropWhileEmpty(t *testing.T) {
106+
t.Parallel()
107+
108+
assert.Empty[int](t, slices.Collect(it.DropWhile(it.Exhausted[int](), filter.Passthrough)))
109+
}
110+
111+
func TestDropWhileNeverDrop(t *testing.T) {
112+
t.Parallel()
113+
114+
numbers := slices.Collect(it.DropWhile(slices.Values([]int{1, 2, 3}), func(int) bool { return false }))
115+
assert.SliceEqual(t, []int{1, 2, 3}, numbers)
116+
}
117+
118+
func TestDropWhileDropAll(t *testing.T) {
119+
t.Parallel()
120+
121+
numbers := slices.Collect(it.DropWhile(slices.Values([]int{1, 2, 3}), filter.LessThan(4)))
122+
assert.Empty[int](t, numbers)
123+
}
124+
125+
func ExampleDropWhile2() {
126+
_, values := it.Collect2(it.DropWhile2(slices.All([]int{1, 2, 3}), func(int, v int) bool {
127+
return v < 3
128+
}))
129+
130+
fmt.Println(values)
131+
// Output: [3]
132+
}
133+
134+
func TestDropWhile2YieldFalse(t *testing.T) {
135+
t.Parallel()
136+
137+
numbers := it.DropWhile2(slices.All([]int{1, 2, 3}), func(int, v int) bool {
138+
return v < 3
139+
})
140+
141+
numbers(func(int, v int) bool {
142+
return false
143+
})
144+
}
145+
146+
func TestDropWhile2Empty(t *testing.T) {
147+
t.Parallel()
148+
149+
_, values := it.Collect2(it.DropWhile2(it.Exhausted2[int, int](), filter.Passthrough2))
150+
151+
assert.Empty[int](t, values)
152+
}
153+
154+
func TestDropWhile2NeverDrop(t *testing.T) {
155+
t.Parallel()
156+
157+
_, values := it.Collect2(it.DropWhile2(slices.All([]int{1, 2, 3}), func(int, int) bool { return false }))
158+
assert.SliceEqual(t, []int{1, 2, 3}, values)
159+
}
160+
161+
func TestDropWhile2DropAll(t *testing.T) {
162+
t.Parallel()
163+
164+
_, values := it.Collect2(it.DropWhile2(slices.All([]int{1, 2, 3}), func(int, int) bool { return true }))
165+
assert.Empty[int](t, values)
166+
}

it/itx/drop.go

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

it/itx/drop_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_Drop() {
2425
fmt.Println(len(maps.Collect(numbers.Seq())))
2526
// Output: 2
2627
}
28+
29+
func ExampleIterator_DropWhile() {
30+
for value := range itx.FromSlice([]int{1, 2, 3, 4, 5}).DropWhile(filter.LessThan(3)) {
31+
fmt.Println(value)
32+
}
33+
34+
// Output:
35+
// 3
36+
// 4
37+
// 5
38+
}
39+
40+
func ExampleIterator2_DropWhile() {
41+
lessThanThree := func(int, v int) bool { return v < 3 }
42+
43+
_, numbers := itx.FromSlice([]int{1, 2, 3, 4, 5}).Enumerate().DropWhile(lessThanThree).Collect()
44+
fmt.Println(numbers)
45+
// Output: [3 4 5]
46+
}

0 commit comments

Comments
 (0)