Skip to content

Commit

Permalink
Indexed funcs to apply
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielseibel1 committed Jun 25, 2024
1 parent 0c88071 commit 0d47d4e
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 4 deletions.
65 changes: 65 additions & 0 deletions apply/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
}
105 changes: 103 additions & 2 deletions apply/map_test.go
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
})
}
}
16 changes: 14 additions & 2 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down

0 comments on commit 0d47d4e

Please sign in to comment.