From 0d47d4e69adfdd5215a652a103786cafa08fb394 Mon Sep 17 00:00:00 2001 From: gabrielseibel1 Date: Tue, 25 Jun 2024 15:03:00 -0300 Subject: [PATCH] Indexed funcs to apply --- apply/map.go | 65 ++++++++++++++++++++++++++++ apply/map_test.go | 105 +++++++++++++++++++++++++++++++++++++++++++++- util/util.go | 16 ++++++- 3 files changed, 182 insertions(+), 4 deletions(-) diff --git a/apply/map.go b/apply/map.go index cd86f38..080d8b8 100644 --- a/apply/map.go +++ b/apply/map.go @@ -2,6 +2,10 @@ // TODO indexed package apply +import "github.com/gabrielseibel1/fungo/types" + +// Unindexed + // ToSlice applies a transforming function to a slice's elements, returning a slice with the return type of the function func ToSlice[T, U any](s []T, f func(T) U) []U { r := make([]U, len(s)) @@ -41,3 +45,64 @@ func ToValues[T comparable, U any, V any](m map[T]U, f func(U) V) map[T]V { } return r } + +// ToPairs applies a transforming function to a slice of pairs, returning another with the return type of the function +func ToPairs[T any, U any, V any, W any](p []types.Pair[T, U], f func(types.Pair[T, U]) types.Pair[V, W]) []types.Pair[V, W] { + q := make([]types.Pair[V, W], len(p)) + for i := range p { + q[i] = f(p[i]) + } + return q +} + +// Indexed + +// ToSliceIndexed is like ToSlice but with the index passed to the application function +func ToSliceIndexed[T, U any](s []T, f func(int, T) U) []U { + r := make([]U, len(s)) + for i := range r { + r[i] = f(i, s[i]) + } + return r +} + +// ToChannelIndexed is like ToChannel but with the index passed to the application function +func ToChannelIndexed[T any, U any](c <-chan T, f func(int, T) U) chan U { + r := make(chan U) + var i int + go func(in <-chan T, out chan<- U) { + for e := range in { + out <- f(i, e) + i++ + } + close(out) + }(c, r) + return r +} + +// ToKeysValued is like ToKeys but with the values passed to the application function +func ToKeysValued[T comparable, U comparable, V any](m map[T]V, f func(T, V) U) map[U]V { + r := make(map[U]V) + for k, v := range m { + r[f(k, v)] = v + } + return r +} + +// ToValueKeyed is like ToValues but with the keys passed to the application function +func ToValuesKeyed[T comparable, U any, V any](m map[T]U, f func(T, U) V) map[T]V { + r := make(map[T]V) + for k, v := range m { + r[k] = f(k, v) + } + return r +} + +// ToPairsIndexed is like ToPairs but with the indexed passed to the application function +func ToPairsIndexed[T any, U any, V any, W any](p []types.Pair[V, U], f func(int, types.Pair[V, U]) types.Pair[V, W]) []types.Pair[V, W] { + q := make([]types.Pair[V, W], len(p)) + for i := range p { + q[i] = f(i, p[i]) + } + return q +} diff --git a/apply/map_test.go b/apply/map_test.go index 71b8230..1db57bb 100644 --- a/apply/map_test.go +++ b/apply/map_test.go @@ -1,11 +1,13 @@ package apply_test import ( - "github.com/gabrielseibel1/fungo/apply" - "github.com/gabrielseibel1/fungo/util" "reflect" "strconv" "testing" + + "github.com/gabrielseibel1/fungo/apply" + "github.com/gabrielseibel1/fungo/types" + "github.com/gabrielseibel1/fungo/util" ) func TestToSlice(t *testing.T) { @@ -61,6 +63,59 @@ func TestToSlice(t *testing.T) { } } +func TestToSliceIndexed(t *testing.T) { + type args[T any, U any] struct { + s []T + f func(int, T) U + } + type testCase[T any, U any] struct { + name string + args args[T, U] + want []U + } + tests := []testCase[util.Data, util.Record]{ + { + name: "empty", + args: args[util.Data, util.Record]{ + s: []util.Data{}, + f: util.DataToRecordIndexed, + }, + want: []util.Record{}, + }, + { + name: "nil", + args: args[util.Data, util.Record]{ + s: nil, + f: util.DataToRecordIndexed, + }, + want: []util.Record{}, + }, + { + name: "one", + args: args[util.Data, util.Record]{ + s: []util.Data{util.Data1}, + f: util.DataToRecordIndexed, + }, + want: []util.Record{util.Record1}, + }, + { + name: "two", + args: args[util.Data, util.Record]{ + s: []util.Data{util.Data1, util.Data2}, + f: util.DataToRecordIndexed, + }, + want: []util.Record{util.Record1, util.Record2}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := apply.ToSliceIndexed(tt.args.s, tt.args.f); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ToSlice() = %v, want %v", got, tt.want) + } + }) + } +} + func TestToKeys(t *testing.T) { type args[T comparable, V any, U comparable] struct { m map[T]V @@ -163,3 +218,49 @@ func TestToChannel(t *testing.T) { } }) } + +func TestToPairs(t *testing.T) { + type args[T, U, V, W any] struct { + p []types.Pair[T, U] + f func(types.Pair[T, U]) types.Pair[V, W] + } + type testCase[T, U, V, W any] struct { + name string + args args[T, U, V, W] + want []types.Pair[V, W] + } + tests := []testCase[int, bool, string, byte]{ + { + name: "int-bool to string-byte", + args: args[int, bool, string, byte]{ + p: []types.Pair[int, bool]{ + {K: 1, V: true}, + {K: 0, V: false}, + }, + f: func(p types.Pair[int, bool]) types.Pair[string, byte] { + var b byte + if p.V { + b = byte(1) + } else { + b = byte(0) + } + return types.Pair[string, byte]{ + K: strconv.Itoa(p.K), + V: b, + } + }, + }, + want: []types.Pair[string, byte]{ + {K: "1", V: byte(1)}, + {K: "0", V: byte(0)}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := apply.ToPairs(tt.args.p, tt.args.f); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ToPairs() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/util/util.go b/util/util.go index 92f6659..4dea8ef 100644 --- a/util/util.go +++ b/util/util.go @@ -72,9 +72,21 @@ func IsIndexed[T any](i int, t T) func(int, T) bool { // DataToRecord gets a Record1 if provided Data1, or Record2 if provided Data2 func DataToRecord(d Data) Record { - if d.V == Data1.V { + switch d.V { + case Data1.V: return Record1 - } else if d.V == Data2.V { + case Data2.V: + return Record2 + default: + return Record{} + } +} + +// DataToRecordIndexed is like DataToRecord but uses the index as well +func DataToRecordIndexed(i int, d Data) Record { + if d.V == Data1.V && i == 0 { + return Record1 + } else if d.V == Data2.V && i == 1 { return Record2 } else { return Record{}