-
-
Notifications
You must be signed in to change notification settings - Fork 830
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement basic set of helpers for iterators
- Loading branch information
Showing
25 changed files
with
4,706 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package la | ||
|
||
import "iter" | ||
|
||
// Reduce reduces an iter.Seq to a value which is the accumulated result of | ||
// running each element in the iter.Seq through accumulator, where each | ||
// successive invocation is supplied with the return value of the previous. | ||
// | ||
// Since this function requires collecting all values from the iterator – in many | ||
// cases, it will be better to use [lo.Reduce] after [slices.Collect] on the | ||
// iterator instead of this function. | ||
func Reduce[T any, R any](collection iter.Seq[T], accumulator func(agg R, item T) R, initial R) R { | ||
for v := range collection { | ||
initial = accumulator(initial, v) | ||
} | ||
|
||
return initial | ||
} | ||
|
||
// Reduce2 reduces an iter.Seq2 to a value which is the accumulated result of | ||
// running each element in the iter.Seq2 through accumulator, where each | ||
// successive invocation is supplied with the return value of the previous. | ||
// | ||
// Since this function requires collecting all values from the iterator – in many | ||
// cases, it will be better to use [lo.Reduce] after [Tuples] and | ||
// [slices.Collect] on the iterator instead of this function. | ||
func Reduce2[K, V any, R any](collection iter.Seq2[K, V], accumulator func(agg R, key K, val V) R, initial R) R { | ||
for k, v := range collection { | ||
initial = accumulator(initial, k, v) | ||
} | ||
|
||
return initial | ||
} | ||
|
||
// ForEach iterates over elements of an iter.Seq and invokes iteratee for each element. | ||
func ForEach[T any](collection iter.Seq[T], iteratee func(item T)) { | ||
for v := range collection { | ||
iteratee(v) | ||
} | ||
} | ||
|
||
// ForEach2 iterates over elements of an iter.Seq2 and invokes iteratee for each element. | ||
func ForEach2[K, V any](collection iter.Seq2[K, V], iteratee func(key K, val V)) { | ||
for k, v := range collection { | ||
iteratee(k, v) | ||
} | ||
} | ||
|
||
// ForEachWhile iterates over elements of an iter.Seq and invokes iteratee for | ||
// each element, the returned value decides to continue or break, like do | ||
// while(). | ||
func ForEachWhile[T any](collection iter.Seq[T], iteratee func(item T) (goon bool)) { | ||
for v := range collection { | ||
if !iteratee(v) { | ||
break | ||
} | ||
} | ||
} | ||
|
||
// ForEachWhile2 iterates over elements of an iter.Seq and invokes iteratee for | ||
// each element, the returned value decides to continue or break, like do | ||
// while(). | ||
func ForEachWhile2[K, V any](collection iter.Seq2[K, V], iteratee func(key K, val V) (goon bool)) { | ||
for k, v := range collection { | ||
if !iteratee(k, v) { | ||
break | ||
} | ||
} | ||
} | ||
|
||
// KeyValues create two parallel iterators where the first yields keys and the | ||
// second – values of the original iterator. | ||
// | ||
// To achieve that, it is necessary to walk through an original iterator twice, | ||
// so if your iterator is not support that – avoid this function. | ||
func KeyValues[K any, V any](in iter.Seq2[K, V]) (iter.Seq[K], iter.Seq[V]) { | ||
return func(yield func(K) bool) { | ||
for k := range in { | ||
if !yield(k) { | ||
return | ||
} | ||
} | ||
}, | ||
func(yield func(V) bool) { | ||
for _, v := range in { | ||
if !yield(v) { | ||
return | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package la | ||
|
||
import ( | ||
"fmt" | ||
"github.com/samber/lo" | ||
"math" | ||
"slices" | ||
) | ||
|
||
func ExampleReduce() { | ||
result := Reduce(slices.Values([]int64{1, 2, 3, 4}), func(agg int64, item int64) int64 { | ||
return agg + item | ||
}, 0) | ||
|
||
fmt.Printf("%v", result) | ||
// Output: 10 | ||
} | ||
|
||
func ExampleReduce2() { | ||
result := Reduce2(Enumerate(slices.Values([]int64{1, 2, 3, 4})), func(agg int64, k int, item int64) int64 { | ||
if k%2 == 0 { | ||
return agg + item | ||
} | ||
|
||
return agg | ||
}, 0) | ||
|
||
fmt.Printf("%v", result) | ||
// Output: 4 | ||
} | ||
|
||
func ExampleForEach() { | ||
ForEach(slices.Values([]int64{1, 2, 3, 4}), func(x int64) { | ||
fmt.Println(x) | ||
}) | ||
|
||
// Output: | ||
// 1 | ||
// 2 | ||
// 3 | ||
// 4 | ||
} | ||
|
||
func ExampleForEach2() { | ||
ForEach2(Enumerate(slices.Values([]int64{1, 2, 3, 4})), func(k int, x int64) { | ||
fmt.Printf("%d %d\n", k, x) | ||
}) | ||
|
||
// Output: | ||
// 0 1 | ||
// 1 2 | ||
// 2 3 | ||
// 3 4 | ||
} | ||
|
||
func ExampleForEachWhile() { | ||
ForEachWhile(slices.Values([]int64{1, 2, -math.MaxInt, 4}), func(x int64) bool { | ||
if x < 0 { | ||
return false | ||
} | ||
fmt.Println(x) | ||
return true | ||
}) | ||
|
||
// Output: | ||
// 1 | ||
// 2 | ||
} | ||
|
||
func ExampleForEachWhile2() { | ||
ForEachWhile2(Enumerate(slices.Values([]int64{1, 2, -math.MaxInt, 4})), func(k int, x int64) bool { | ||
if x < 0 { | ||
return false | ||
} | ||
fmt.Printf("%d %d\n", k, x) | ||
return true | ||
}) | ||
|
||
// Output: | ||
// 0 1 | ||
// 1 2 | ||
} | ||
|
||
func ExampleKeyValues() { | ||
kv := FromTuples([]lo.Tuple2[string, int]{ | ||
{"foo", 1}, | ||
{"bar", 2}, | ||
{"baz", 3}, | ||
}) | ||
|
||
keys, values := KeyValues(kv) | ||
|
||
fmt.Printf("%v %v", Collect(keys), Collect(values)) | ||
// Output: [foo bar baz] [1 2 3] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package la | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"slices" | ||
"testing" | ||
) | ||
|
||
func TestReduce(t *testing.T) { | ||
t.Parallel() | ||
is := assert.New(t) | ||
|
||
result1 := Reduce(slices.Values([]int{1, 2, 3, 4}), func(agg int, item int) int { | ||
return agg + item | ||
}, 0) | ||
result2 := Reduce(slices.Values([]int{1, 2, 3, 4}), func(agg int, item int) int { | ||
return agg + item | ||
}, 10) | ||
|
||
is.Equal(result1, 10) | ||
is.Equal(result2, 20) | ||
} | ||
|
||
func TestReduce2(t *testing.T) { | ||
t.Parallel() | ||
is := assert.New(t) | ||
|
||
result1 := Reduce2(Enumerate(slices.Values([]int{1, 2, 3, 4})), func(agg int, _ int, item int) int { | ||
return agg + item | ||
}, 0) | ||
result2 := Reduce2(Enumerate(slices.Values([]int{1, 2, 3, 4})), func(agg int, _ int, item int) int { | ||
return agg + item | ||
}, 10) | ||
|
||
is.Equal(result1, 10) | ||
is.Equal(result2, 20) | ||
} | ||
|
||
func TestForEach(t *testing.T) { | ||
t.Parallel() | ||
is := assert.New(t) | ||
|
||
// check of callback is called for every element and in proper order | ||
|
||
callParams1 := []string{} | ||
|
||
ForEach(slices.Values([]string{"a", "b", "c"}), func(item string) { | ||
callParams1 = append(callParams1, item) | ||
}) | ||
|
||
is.ElementsMatch([]string{"a", "b", "c"}, callParams1) | ||
} | ||
|
||
func TestForEach2(t *testing.T) { | ||
t.Parallel() | ||
is := assert.New(t) | ||
|
||
// check of callback is called for every element and in proper order | ||
|
||
callParams1 := []string{} | ||
callParams2 := []int{} | ||
|
||
ForEach2(Enumerate(slices.Values([]string{"a", "b", "c"})), func(i int, item string) { | ||
callParams1 = append(callParams1, item) | ||
callParams2 = append(callParams2, i) | ||
}) | ||
|
||
is.ElementsMatch([]string{"a", "b", "c"}, callParams1) | ||
is.ElementsMatch([]int{0, 1, 2}, callParams2) | ||
is.IsIncreasing(callParams2) | ||
} | ||
|
||
func TestForEachWhile(t *testing.T) { | ||
t.Parallel() | ||
is := assert.New(t) | ||
|
||
// check of callback is called for every element and in proper order | ||
|
||
var callParams1 []string | ||
|
||
ForEachWhile(slices.Values([]string{"a", "b", "c"}), func(item string) bool { | ||
if item == "c" { | ||
return false | ||
} | ||
callParams1 = append(callParams1, item) | ||
|
||
return true | ||
}) | ||
|
||
is.ElementsMatch([]string{"a", "b"}, callParams1) | ||
} | ||
|
||
func TestForEachWhile2(t *testing.T) { | ||
t.Parallel() | ||
is := assert.New(t) | ||
|
||
// check of callback is called for every element and in proper order | ||
|
||
var callParams1 []string | ||
var callParams2 []int | ||
|
||
ForEachWhile2(Enumerate(slices.Values([]string{"a", "b", "c"})), func(i int, item string) bool { | ||
if item == "c" { | ||
return false | ||
} | ||
callParams1 = append(callParams1, item) | ||
callParams2 = append(callParams2, i) | ||
return true | ||
}) | ||
|
||
is.ElementsMatch([]string{"a", "b"}, callParams1) | ||
is.ElementsMatch([]int{0, 1}, callParams2) | ||
is.IsIncreasing(callParams2) | ||
} |
Oops, something went wrong.