diff --git a/README.md b/README.md index 9072df8..366e65d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # FuncFrog -[![Go Report Card](https://goreportcard.com/badge/github.com/koss-null/funcfrog)](https://goreportcard.com/report/github.com/koss-null/lambda) +[![Go Report Card](https://goreportcard.com/badge/github.com/koss-null/funcfrog)](https://goreportcard.com/report/github.com/koss-null/funcfrog) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Coverage](https://raw.githubusercontent.com/koss-null/funcfrog/master/coverage_badge.png?raw=true)](coverage) diff --git a/coverage_badge.png b/coverage_badge.png index 22cc9fa..30891f7 100644 Binary files a/coverage_badge.png and b/coverage_badge.png differ diff --git a/internal/internalpipe/constructor.go b/internal/internalpipe/constructor.go index 0a4bd71..e263fe1 100644 --- a/internal/internalpipe/constructor.go +++ b/internal/internalpipe/constructor.go @@ -12,17 +12,14 @@ const ( ) func Slice[T any](dt []T) Pipe[T] { - dtCp := make([]T, len(dt)) - copy(dtCp, dt) - return Pipe[T]{ Fn: func(i int) (*T, bool) { - if i >= len(dtCp) { + if i >= len(dt) { return nil, true } - return &dtCp[i], false + return &dt[i], false }, - Len: len(dtCp), + Len: len(dt), ValLim: notSet, GoroutinesCnt: defaultParallelWrks, } diff --git a/pkg/ff/ff_test.go b/pkg/ff/ff_test.go index 630b853..4a32393 100644 --- a/pkg/ff/ff_test.go +++ b/pkg/ff/ff_test.go @@ -41,6 +41,38 @@ func TestMap(t *testing.T) { } } +func TestMapFilter(t *testing.T) { + a := []int{1, 2, 3, 4, 5, 6, 7, 8} + + fn := func(x int) (string, bool) { + return strconv.Itoa(x), x%2 == 0 + } + + piper := MapFilter(a, fn).Do() + + // Iterate through the values and test the output + expected := []string{"2", "4", "6", "8"} + for i, val := range piper { + require.Equal(t, expected[i], val, "Unexpected result for MapFilter") + } +} + +func TestFilter(t *testing.T) { + a := []int{1, 2, 3, 4, 5} + + fn := func(x *int) bool { + return *x%2 != 0 + } + + piper := Filter(a, fn).Do() + + // Iterate through the values and test the output + expected := []int{1, 3, 5} + for i, val := range piper { + require.Equal(t, expected[i], val, "Unexpected result for Filter") + } +} + func TestReduce(t *testing.T) { a := []int{1, 2, 3, 4, 5} diff --git a/pkg/ff/filter.go b/pkg/ff/filter.go new file mode 100644 index 0000000..80506a2 --- /dev/null +++ b/pkg/ff/filter.go @@ -0,0 +1,8 @@ +package ff + +import "github.com/koss-null/funcfrog/pkg/pipe" + +// Filter is a short way to create a Pipe from a slice of SrcT applying Filter function fn. +func Filter[SrcT any](a []SrcT, fn func(*SrcT) bool) pipe.Piper[SrcT] { + return pipe.Slice(a).Filter(fn) +} diff --git a/pkg/ff/map_filter.go b/pkg/ff/map_filter.go new file mode 100644 index 0000000..3c5df25 --- /dev/null +++ b/pkg/ff/map_filter.go @@ -0,0 +1,8 @@ +package ff + +import "github.com/koss-null/funcfrog/pkg/pipe" + +// MapFilter is a short way to create a Pipe of DstT from a slice of SrcT applying MapFilter function fn. +func MapFilter[SrcT, DstT any](a []SrcT, fn func(SrcT) (DstT, bool)) pipe.Piper[DstT] { + return pipe.MapFilter(pipe.Slice(a), fn) +} diff --git a/pkg/pipe/pipe_test.go b/pkg/pipe/pipe_test.go index 64aec1f..f8a784a 100644 --- a/pkg/pipe/pipe_test.go +++ b/pkg/pipe/pipe_test.go @@ -1075,6 +1075,33 @@ func TestPrefixMapNL(t *testing.T) { require.Equal(t, []string{"1", "3", "4"}, res) } +func TestPrefixMapFilter(t *testing.T) { + res := pipe.MapFilter( + pipe.Slice([]int{1, 2, 3, 4, 5}).Filter( + func(x *int) bool { return *x != 5 }, + ), + func(x int) (string, bool) { + return strconv.Itoa(x), x != 2 + }, + ).Do() + require.Equal(t, []string{"1", "3", "4"}, res) +} + +func TestPrefixMapFilterNL(t *testing.T) { + t.Parallel() + + res := pipe.MapFilterNL( + pipe.Func(func(i int) (int, bool) { + a := [...]int{1, 2, 3, 4, 5} + return a[i], a[i] != 5 + }), + func(x int) (string, bool) { + return strconv.Itoa(x), x != 2 + }, + ).Gen(5).Do() + require.Equal(t, []string{"1", "3", "4"}, res) +} + func TestPrefixReduce(t *testing.T) { t.Parallel() diff --git a/pkg/pipe/prefixpipe.go b/pkg/pipe/prefixpipe.go index 6a572bd..373f8ff 100644 --- a/pkg/pipe/prefixpipe.go +++ b/pkg/pipe/prefixpipe.go @@ -27,7 +27,7 @@ func Map[SrcT any, DstT any]( } // MapNL applies function on a PiperNoLen of type SrcT and returns a Pipe of type DstT. -func MapNL[SrcT any, DstT any]( +func MapNL[SrcT, DstT any]( p PiperNoLen[SrcT], fn func(x SrcT) DstT, ) PiperNoLen[DstT] { @@ -46,9 +46,51 @@ func MapNL[SrcT any, DstT any]( }} } +// MapFilter applies function on a Piper of type SrcT and returns a Pipe of type DstT. +// fn returns a value of DstT type and true if this value is not skipped. +func MapFilter[SrcT, DstT any]( + p Piper[SrcT], + fn func(x SrcT) (DstT, bool), +) Piper[DstT] { + pp := any(p).(entrails[SrcT]).Entrails() + return &Pipe[DstT]{internalpipe.Pipe[DstT]{ + Fn: func(i int) (*DstT, bool) { + if obj, skipped := pp.Fn(i); !skipped { + dst, exist := fn(*obj) + return &dst, !exist + } + return nil, true + }, + Len: pp.Len, + ValLim: pp.ValLim, + GoroutinesCnt: pp.GoroutinesCnt, + }} +} + +// MapFilterNL applies function on a PiperNoLen of type SrcT and returns a Pipe of type DstT. +// fn returns a value of DstT type and true if this value is not skipped. +func MapFilterNL[SrcT, DstT any]( + p PiperNoLen[SrcT], + fn func(x SrcT) (DstT, bool), +) PiperNoLen[DstT] { + pp := any(p).(entrails[SrcT]).Entrails() + return &PipeNL[DstT]{internalpipe.Pipe[DstT]{ + Fn: func(i int) (*DstT, bool) { + if obj, skipped := pp.Fn(i); !skipped { + dst, exist := fn(*obj) + return &dst, !exist + } + return nil, true + }, + Len: pp.Len, + ValLim: pp.ValLim, + GoroutinesCnt: pp.GoroutinesCnt, + }} +} + // Reduce applies reduce operation on Pipe of type SrcT and returns result of type DstT. // initVal is an optional parameter to initialize a value that should be used on the first step of reduce. -func Reduce[SrcT any, DstT any](p Piper[SrcT], fn func(*DstT, *SrcT) DstT, initVal ...DstT) DstT { +func Reduce[SrcT, DstT any](p Piper[SrcT], fn func(*DstT, *SrcT) DstT, initVal ...DstT) DstT { var init DstT if len(initVal) > 0 { init = initVal[0]