Skip to content

Commit

Permalink
Add filter slice with indices
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielseibel1 committed Sep 6, 2023
1 parent 971f2ae commit d42eba0
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 2 deletions.
17 changes: 15 additions & 2 deletions filter/filter.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Package filter has functions to filter slices, maps, and channels by some condition
// TODO indexed
package filter

// Slice filters a slice by a function, returning another slice with only the elements that satisfy the function condition
// Slice filters a slice by a function that takes an element an returns a bool,
// returning another slice with only the elements that satisfy the function condition
func Slice[T any](s []T, f func(T) bool) []T {
r := make([]T, 0)
for _, v := range s {
Expand All @@ -13,6 +13,18 @@ func Slice[T any](s []T, f func(T) bool) []T {
return r
}

// SliceIndexed filters a slice by a function that takes an index and an element and returns a bool,
// returning another slice with only the elements that satisfy the function condition
func SliceIndexed[T any](s []T, f func(int, T) bool) []T {
r := make([]T, 0)
for i, v := range s {
if f(i, v) {
r = append(r, v)
}
}
return r
}

// Channel filters a channel by a function, returning another channel with only the elements that satisfy the function condition
// Does not consume the whole channel, works "on demand"
func Channel[T any](c <-chan T, f func(T) bool) chan T {
Expand All @@ -29,6 +41,7 @@ func Channel[T any](c <-chan T, f func(T) bool) chan T {
}

// MapByKeys filters a map by a function, returning another map with only the keys satisfy the function condition
// TODO just "Map", with keys and values passed to func
func MapByKeys[T comparable, U any](m map[T]U, f func(T) bool) map[T]U {
r := make(map[T]U)
for k, v := range m {
Expand Down
76 changes: 76 additions & 0 deletions filter/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,82 @@ func TestSlice(t *testing.T) {
}
}

func TestSliceIndexed(t *testing.T) {
type args[T any] struct {
s []T
f func(int, T) bool
}
type testCase[T any] struct {
name string
args args[T]
want []T
}
tests := []testCase[util.Data]{
{
name: "empty",
args: args[util.Data]{
f: util.PassAllIndexed[util.Data],
},
want: []util.Data{},
},
{
name: "one element (1), pass all",
args: args[util.Data]{
s: []util.Data{util.Data1},
f: util.PassAllIndexed[util.Data],
},
want: []util.Data{util.Data1},
},
{
name: "one element (2), pass all",
args: args[util.Data]{
s: []util.Data{util.Data1},
f: util.PassAllIndexed[util.Data],
},
want: []util.Data{util.Data1},
},
{
name: "two elements, pass all",
args: args[util.Data]{
s: []util.Data{util.Data1, util.Data2},
f: util.PassAllIndexed[util.Data],
},
want: []util.Data{util.Data1, util.Data2},
},
{
name: "two elements, pass none",
args: args[util.Data]{
s: []util.Data{util.Data1, util.Data2},
f: util.PassNoIndexed[util.Data],
},
want: []util.Data{},
},
{
name: "two elements, pass first",
args: args[util.Data]{
s: []util.Data{util.Data1, util.Data2},
f: util.IsIndexed(0, util.Data1),
},
want: []util.Data{util.Data1},
},
{
name: "two elements, pass second",
args: args[util.Data]{
s: []util.Data{util.Data1, util.Data2},
f: util.IsIndexed(1, util.Data2),
},
want: []util.Data{util.Data2},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := filter.SliceIndexed(tt.args.s, tt.args.f); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Slice() = %v, want %v", got, tt.want)
}
})
}
}

func TestMapByKeys(t *testing.T) {
type args[T comparable, U any] struct {
m map[T]U
Expand Down
13 changes: 13 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,29 @@ var (
// PassAll is a function that always returns true for any type and value provided, useful for tests
func PassAll[T any](T) bool { return true }

// PassAllIndexed is a function that always returns true for any type and value provided, useful for tests
func PassAllIndexed[T any](int, T) bool { return true }

// PassNo is a function that always returns false for any type and value provided, useful for tests
func PassNo[T any](T) bool { return false }

// PassNoIndexed is a function that always returns false for any type and value provided, useful for tests
func PassNoIndexed[T any](int, T) bool { return false }

// Is can be used to know whether an element is equal to another by reflect.DeepEqual
func Is[T any](t T) func(T) bool {
return func(u T) bool {
return reflect.DeepEqual(t, u)
}
}

// IsIndexed can be used to know whether an element is equal to another by reflect.DeepEqual as is in an index i
func IsIndexed[T any](i int, t T) func(int, T) bool {
return func(j int, u T) bool {
return j == i && reflect.DeepEqual(t, u)
}
}

// DataToRecord gets a Record1 if provided Data1, or Record2 if provided Data2
func DataToRecord(d Data) Record {
if d.V == Data1.V {
Expand Down
141 changes: 141 additions & 0 deletions util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,144 @@ func TestIs(t *testing.T) {
})
}
}

func TestPassAllIndexed(t *testing.T) {
type args[T any] struct {
in0 int
in1 T
}
type testCase[T any] struct {
name string
args args[T]
want bool
}
tests := []testCase[string]{
{
name: "zero",
args: args[string]{},
want: true,
},
{
name: "random",
args: args[string]{
in0: 42,
in1: "42and87",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := util.PassAllIndexed(tt.args.in0, tt.args.in1); got != tt.want {
t.Errorf("PassAllIndexed() = %v, want %v", got, tt.want)
}
})
}
}

func TestPassNoIndexed(t *testing.T) {
type args[T any] struct {
in0 int
in1 T
}
type testCase[T any] struct {
name string
args args[T]
want bool
}
tests := []testCase[string]{
{
name: "zero",
args: args[string]{},
want: false,
},
{
name: "random",
args: args[string]{
in0: 42,
in1: "42and87",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := util.PassNoIndexed(tt.args.in0, tt.args.in1); got != tt.want {
t.Errorf("PassAllIndexed() = %v, want %v", got, tt.want)
}
})
}
}

func TestIsIndexed(t *testing.T) {
type args[T any] struct {
i int
t T
}
type testCase[T any] struct {
name string
args args[T]
j int
u T
want bool
}
tests := []testCase[util.Data]{
{
name: "data 1",
args: args[util.Data]{
i: 0,
t: util.Data1,
},
j: 0,
u: util.Data1,
want: true,
},
{
name: "data 2",
args: args[util.Data]{
i: 1,
t: util.Data2,
},
j: 1,
u: util.Data2,
want: true,
},
{
name: "mismatch index",
args: args[util.Data]{
i: 0,
t: util.Data1,
},
j: 1,
u: util.Data1,
want: false,
},
{
name: "mismatch elements",
args: args[util.Data]{
i: 0,
t: util.Data1,
},
j: 0,
u: util.Data2,
want: false,
},
{
name: "mismatch all",
args: args[util.Data]{
i: 0,
t: util.Data1,
},
j: 1,
u: util.Data2,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := util.IsIndexed(tt.args.i, tt.args.t)(tt.j, tt.u); !got == tt.want {
t.Errorf("IsIndexed() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit d42eba0

Please sign in to comment.