Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: range-over-func POC #439

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 77 additions & 0 deletions iterator.go
@@ -0,0 +1,77 @@
//go:build goexperiment.rangefunc

package lo

// Iterator TODO
type Iterator[V any] func(func(int, V) bool)

// ToIterator TODO
func ToIterator[V any](collection ...V) Iterator[V] {
return func(yield func(int, V) bool) {
for i, item := range collection {
yield(i, item)
}
}
}

// Len TODO
func (iter Iterator[V]) Len() int {
var n int
for _, _ = range iter {
n++
}
return n
}

// Slice TODO
func (iter Iterator[V]) Slice() []V {
var result []V
for _, item := range iter {
result = append(result, item)
}
return result
}

// FilterI returns an iterator of all elements that match the specified predicate callback.
// Play: https://go.dev/play/p/TODO
func FilterI[V any](iter Iterator[V], predicate func(item V, index int) bool) Iterator[V] {
return func(yield func(int, V) bool) {
for i, item := range iter {
if predicate(item, i) {
if !yield(i, item) {
break
}
}
}
}
}

// MapI transforms an iterator into an iterator of another type.
// Play: https://go.dev/play/p/TODO
func MapI[T any, R any](iter Iterator[T], iteratee func(item T, index int) R) Iterator[R] {
return func(yield func(int, R) bool) {
for i, item := range iter {
if !yield(i, iteratee(item, i)) {
break
}
}
}
}

// FilterMapI returns an iterator obtained after both filtering and mapping using the given callback function.
// The callback function should return two values:
// - the result of the mapping operation and
// - whether the result element should be included or not.
//
// Play: https://go.dev/play/p/TODO
func FilterMapI[T any, R any](iter Iterator[T], callback func(item T, index int) (R, bool)) Iterator[R] {
return func(yield func(int, R) bool) {
for i, item := range iter {
if r, ok := callback(item, i); ok {
if !yield(i, r) {
break
}
}
}
}
}
41 changes: 41 additions & 0 deletions iterator_example_test.go
@@ -0,0 +1,41 @@
//go:build goexperiment.rangefunc

package lo

import (
"fmt"
"strconv"
)

func ExampleFilterI() {
iter := ToIterator(1, 2, 3, 4)

result := FilterI(iter, func(nbr int, index int) bool {
return nbr%2 == 0
})

fmt.Printf("%v", result.Slice())
// Output: [2 4]
}

func ExampleMapI() {
iter := ToIterator(1, 2, 3, 4)

result := MapI(iter, func(nbr int, index int) string {
return strconv.FormatInt(int64(nbr)*2, 10)
})

fmt.Printf("%v", result.Slice())
// Output: [2 4 6 8]
}

func ExampleFilterMapI() {
iter := ToIterator(1, 2, 3, 4)

result := FilterMapI(iter, func(nbr int, index int) (string, bool) {
return strconv.FormatInt(int64(nbr)*2, 10), nbr%2 == 0
})

fmt.Printf("%v", result.Slice())
// Output: [4 8]
}
68 changes: 68 additions & 0 deletions iterator_test.go
@@ -0,0 +1,68 @@
//go:build goexperiment.rangefunc

package lo

import (
"strconv"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestFilterI(t *testing.T) {
t.Parallel()
is := assert.New(t)

r1 := FilterI(ToIterator(1, 2, 3, 4), func(x int, _ int) bool {
return x%2 == 0
})

is.Equal(r1.Slice(), []int{2, 4})

r2 := FilterI(ToIterator("", "foo", "", "bar", ""), func(x string, _ int) bool {
return len(x) > 0
})

is.Equal(r2.Slice(), []string{"foo", "bar"})
}

func TestMapI(t *testing.T) {
t.Parallel()
is := assert.New(t)

result1 := MapI(ToIterator(1, 2, 3, 4), func(x int, _ int) string {
return "Hello"
})
result2 := MapI(ToIterator(1, 2, 3, 4), func(x int, _ int) string {
return strconv.FormatInt(int64(x), 10)
})

is.Equal(result1.Len(), 4)
is.Equal(result2.Len(), 4)
is.Equal(result1.Slice(), []string{"Hello", "Hello", "Hello", "Hello"})
is.Equal(result2.Slice(), []string{"1", "2", "3", "4"})
}

func TestFilterMapI(t *testing.T) {
t.Parallel()
is := assert.New(t)

r1 := FilterMapI(ToIterator(1, 2, 3, 4), func(x int, _ int) (string, bool) {
if x%2 == 0 {
return strconv.FormatInt(int64(x), 10), true
}
return "", false
})
r2 := FilterMapI(ToIterator("cpu", "gpu", "mouse", "keyboard"), func(x string, _ int) (string, bool) {
if strings.HasSuffix(x, "pu") {
return "xpu", true
}
return "", false
})

is.Equal(r1.Len(), 2)
is.Equal(r2.Len(), 2)
is.Equal(r1.Slice(), []string{"2", "4"})
is.Equal(r2.Slice(), []string{"xpu", "xpu"})
}