From 7eaa52eaea394bbc0b68ac306a23acca19caad17 Mon Sep 17 00:00:00 2001 From: Tom Godkin Date: Sat, 27 Jul 2024 09:30:02 +0100 Subject: [PATCH] Add TryCollect and Collect2 consumers and remove CollectErr --- it/enumerate_test.go | 22 ++++++++++++++++++++++ it/exhausted_test.go | 22 ---------------------- it/iter.go | 34 ++++++++++++++++++++++++---------- it/iter_test.go | 38 ++++++++++++++++++++++++++++++-------- it/itx/iter.go | 14 ++++++++++---- it/itx/iter_test.go | 17 ++++++++++++++--- 6 files changed, 100 insertions(+), 47 deletions(-) diff --git a/it/enumerate_test.go b/it/enumerate_test.go index 750cf07..d39f37c 100644 --- a/it/enumerate_test.go +++ b/it/enumerate_test.go @@ -3,7 +3,9 @@ package it_test import ( "fmt" "slices" + "testing" + "github.com/BooleanCat/go-functional/v2/internal/assert" "github.com/BooleanCat/go-functional/v2/it" ) @@ -17,3 +19,23 @@ func ExampleEnumerate() { // 1 2 // 2 3 } + +func TestEnumerateYieldFalse(t *testing.T) { + t.Parallel() + + iterator := it.Enumerate(slices.Values([]int{1, 2, 3, 4, 5})) + + var ( + index int + number int + ) + + iterator(func(i int, n int) bool { + index = i + number = n + return false + }) + + assert.Equal(t, index, 0) + assert.Equal(t, number, 1) +} diff --git a/it/exhausted_test.go b/it/exhausted_test.go index a58643d..7f63dff 100644 --- a/it/exhausted_test.go +++ b/it/exhausted_test.go @@ -4,9 +4,7 @@ import ( "fmt" "maps" "slices" - "testing" - "github.com/BooleanCat/go-functional/v2/internal/assert" "github.com/BooleanCat/go-functional/v2/it" ) @@ -19,23 +17,3 @@ func ExampleExhausted2() { fmt.Println(len(maps.Collect(it.Exhausted2[int, string]()))) // Output: 0 } - -func TestEnumerateYieldFalse(t *testing.T) { - t.Parallel() - - iterator := it.Enumerate(slices.Values([]int{1, 2, 3, 4, 5})) - - var ( - index int - number int - ) - - iterator(func(i int, n int) bool { - index = i - number = n - return false - }) - - assert.Equal(t, index, 0) - assert.Equal(t, number, 1) -} diff --git a/it/iter.go b/it/iter.go index 71ed789..0730fb7 100644 --- a/it/iter.go +++ b/it/iter.go @@ -2,7 +2,6 @@ package it import ( "cmp" - "errors" "iter" ) @@ -127,20 +126,35 @@ func Find2[V, W any](iterator func(func(V, W) bool), pred func(V, W) bool) (V, W return zeroV, zeroW, false } -// CollectErr consumes an [iter.Seq2] where the right side yields errors and -// returns a slice of values and all errors joined together. -func CollectErr[V any](delegate func(func(V, error) bool)) ([]V, error) { +// TryCollect consumes an [iter.Seq2] where the right side yields errors and +// returns a slice of values and the first error encountered. Iteration stops +// at the first error. +func TryCollect[V any](iterator func(func(V, error) bool)) ([]V, error) { + var values []V + + for v, err := range iterator { + if err != nil { + return values, err + } + values = append(values, v) + } + + return values, nil +} + +// Collect2 consumes an [iter.Seq2] and returns two slices of values. +func Collect2[V, W any](iterator func(func(V, W) bool)) ([]V, []W) { var ( - values []V - errs []error + lefts []V + rights []W ) - for v, err := range delegate { - values = append(values, v) - errs = append(errs, err) + for v, w := range iterator { + lefts = append(lefts, v) + rights = append(rights, w) } - return values, errors.Join(errs...) + return lefts, rights } // Len consumes an [iter.Seq] and returns the number of values yielded. diff --git a/it/iter_test.go b/it/iter_test.go index 61b2ae4..853fd3d 100644 --- a/it/iter_test.go +++ b/it/iter_test.go @@ -30,7 +30,7 @@ func TestForEachEmpty(t *testing.T) { } func ExampleForEach2() { - it.ForEach2(it.Enumerate(slices.Values([]int{1, 2, 3})), func(index int, number int) { + it.ForEach2(slices.All([]int{1, 2, 3}), func(index int, number int) { fmt.Println(index, number) }) // Output: @@ -59,7 +59,7 @@ func TestReduceEmpty(t *testing.T) { } func ExampleReduce2() { - fmt.Println(it.Reduce2(it.Enumerate(slices.Values([]int{1, 2, 3})), func(i, a, b int) int { + fmt.Println(it.Reduce2(slices.All([]int{1, 2, 3}), func(i, a, b int) int { return i + 1 }, 0)) @@ -115,7 +115,7 @@ func ExampleFind_notFound() { } func ExampleFind2() { - index, value, ok := it.Find2(it.Enumerate(slices.Values([]int{1, 2, 3})), func(i, v int) bool { + index, value, ok := it.Find2(slices.All([]int{1, 2, 3}), func(i, v int) bool { return i == 2 }) fmt.Println(index, value, ok) @@ -124,7 +124,7 @@ func ExampleFind2() { } func ExampleFind2_notFound() { - index, value, ok := it.Find2(it.Enumerate(slices.Values([]int{1, 2, 3})), func(i, v int) bool { + index, value, ok := it.Find2(slices.All([]int{1, 2, 3}), func(i, v int) bool { return i == 4 }) @@ -132,16 +132,38 @@ func ExampleFind2_notFound() { // Output: 0 0 false } -func ExampleCollectErr() { - data := strings.NewReader("one\ntwo\nthree\n") - lines, err := it.CollectErr(it.LinesString(data)) +func ExampleCollect2() { + indicies, values := it.Collect2(slices.All([]int{1, 2, 3})) + fmt.Println(values) + fmt.Println(indicies) + + // Output: + // [1 2 3] + // [0 1 2] +} + +func ExampleTryCollect() { + text := strings.NewReader("one\ntwo\nthree\n") + + lines, err := it.TryCollect(it.LinesString(text)) fmt.Println(err) fmt.Println(lines) + // Output: // // [one two three] } +func TestTryCollectError(t *testing.T) { + t.Parallel() + + text := new(failSecondTime) + lines, err := it.TryCollect(it.LinesString(text)) + + assert.Equal(t, err.Error(), "read error") + assert.SliceEqual(t, lines, []string{"o"}) +} + func ExampleLen() { fmt.Println(it.Len(slices.Values([]int{1, 2, 3}))) @@ -155,7 +177,7 @@ func TestLenEmpty(t *testing.T) { } func ExampleLen2() { - fmt.Println(it.Len2(it.Enumerate(slices.Values([]int{1, 2, 3})))) + fmt.Println(it.Len2(slices.All([]int{1, 2, 3}))) // Output: 3 } diff --git a/it/itx/iter.go b/it/itx/iter.go index 6bfac04..2c0e29a 100644 --- a/it/itx/iter.go +++ b/it/itx/iter.go @@ -74,10 +74,16 @@ func (iterator Iterator2[V, W]) Find(predicate func(V, W) bool) (V, W, bool) { return it.Find2(iterator, predicate) } -// CollectErr consumes an [Iterator2] where the right side yields errors and -// returns a slice of values and all errors yielded joined together. -func CollectErr[V any](iterator Iterator2[V, error]) ([]V, error) { - return it.CollectErr(iter.Seq2[V, error](iterator)) +// TryCollect consumes an [iter.Seq2] where the right side yields errors and +// returns a slice of values and the first error encountered. Iteration stops +// at the first error. +func TryCollect[V any](iterator func(func(V, error) bool)) ([]V, error) { + return it.TryCollect(iterator) +} + +// Collect2 consumes an [iter.Seq2] and returns two slices of values. +func (iterator Iterator2[V, W]) Collect() ([]V, []W) { + return it.Collect2(iterator) } // Len is a convenience method for chaining [it.Len] on [Iterator]s. diff --git a/it/itx/iter_test.go b/it/itx/iter_test.go index f91097c..bc0ebb3 100644 --- a/it/itx/iter_test.go +++ b/it/itx/iter_test.go @@ -79,16 +79,27 @@ func ExampleFromMap() { // Output: [2] } -func ExampleCollectErr() { - data := strings.NewReader("one\ntwo\nthree\n") - lines, err := itx.CollectErr(itx.LinesString(data)) +func ExampleTryCollect() { + text := strings.NewReader("one\ntwo\nthree\n") + lines, err := itx.TryCollect(itx.LinesString(text)) fmt.Println(err) fmt.Println(lines) + // Output: // // [one two three] } +func ExampleIterator2_Collect() { + indicies, values := itx.FromSlice([]int{1, 2, 3}).Enumerate().Collect() + fmt.Println(values) + fmt.Println(indicies) + + // Output: + // [1 2 3] + // [0 1 2] +} + func ExampleIterator_Len() { fmt.Println(itx.FromSlice([]int{1, 2, 3}).Len()) // Output: 3