Skip to content

Commit 0598b75

Browse files
committed
Add filter function
1 parent f7c45b7 commit 0598b75

File tree

4 files changed

+182
-17
lines changed

4 files changed

+182
-17
lines changed

common.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package godash
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
9+
func validateOut(output reflect.Value) error {
10+
zeroValue := reflect.Value{}
11+
if output == zeroValue {
12+
return fmt.Errorf("output is nil. Pass a reference to set output")
13+
}
14+
15+
if output.IsNil() {
16+
return fmt.Errorf("output is nil. Pass a reference to set output")
17+
}
18+
19+
if !output.Elem().CanSet() {
20+
return fmt.Errorf("cannot set out. Pass a reference to set output")
21+
}
22+
23+
return nil
24+
}

filter.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package godash
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
func Filter(in, out, predicateFn interface{}) error {
9+
input := reflect.ValueOf(in)
10+
11+
output := reflect.ValueOf(out)
12+
if err := validateOut(output); err != nil {
13+
return err
14+
}
15+
if input.Type() != output.Elem().Type() {
16+
return fmt.Errorf("input(%s) and output(%s) should be of the same Type", input.Type(), output.Elem().Type())
17+
}
18+
19+
predicate := reflect.ValueOf(predicateFn)
20+
if predicate.Type().NumOut() != 1 {
21+
return fmt.Errorf("predicate function should return only one return value - a boolean")
22+
}
23+
if predicateType := predicate.Type().Out(0).Kind(); predicateType != reflect.Bool {
24+
return fmt.Errorf("predicate function should return only a (boolean) and not a (%s)", predicateType)
25+
}
26+
27+
if input.Kind() == reflect.Slice {
28+
{
29+
if input.Type().Elem().Kind() != predicate.Type().In(0).Kind() {
30+
return fmt.Errorf(
31+
"predicate function's first argument has to be the type (%s) instead of (%s)",
32+
input.Type().Elem(),
33+
predicate.Type().In(0),
34+
)
35+
}
36+
}
37+
38+
result := reflect.MakeSlice(output.Elem().Type(), 0, input.Len())
39+
for i := 0; i < input.Len(); i++ {
40+
arg := input.Index(i)
41+
42+
returnValues := predicate.Call([]reflect.Value{arg})
43+
predicatePassed := returnValues[0].Bool()
44+
45+
if predicatePassed {
46+
result = reflect.Append(result, arg)
47+
}
48+
}
49+
output.Elem().Set(result)
50+
51+
return nil
52+
}
53+
return fmt.Errorf("not implemented")
54+
}

filter_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package godash_test
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"github.com/thecasualcoder/godash"
6+
"testing"
7+
)
8+
9+
func TestFilter(t *testing.T) {
10+
t.Run("should filter elements that fail predicate", func(t *testing.T) {
11+
input := []int{1, 2, 3, 4, 5, 6, 7, 8}
12+
var output []int
13+
14+
err := godash.Filter(input, &output, func(a int) bool {
15+
return a%2 == 0
16+
})
17+
expected := []int{2, 4, 6, 8}
18+
19+
assert.NoError(t, err)
20+
assert.Equal(t, expected, output)
21+
})
22+
23+
t.Run("should struct types", func(t *testing.T) {
24+
type person struct {
25+
name string
26+
age int
27+
}
28+
input := []person{
29+
{"john", 30},
30+
{"doe", 20},
31+
{"foo", 40},
32+
{"bar", 10},
33+
}
34+
var output []person
35+
36+
err := godash.Filter(input, &output, func(p person) bool {
37+
return p.age > 20
38+
})
39+
expected := []person{
40+
{"john", 30},
41+
{"foo", 40},
42+
}
43+
44+
assert.NoError(t, err)
45+
assert.Equal(t, expected, output)
46+
})
47+
48+
t.Run("should validate predicate's arg", func(t *testing.T) {
49+
input := []int{1, 2, 3, 4, 5, 6, 7, 8}
50+
var output []int
51+
52+
err := godash.Filter(input, &output, func(a string) bool {
53+
return a == ""
54+
})
55+
56+
assert.EqualError(t, err, "predicate function's first argument has to be the type (int) instead of (string)")
57+
})
58+
59+
t.Run("should validate predicate's return type", func(t *testing.T) {
60+
input := []int{1, 2, 3, 4, 5, 6, 7, 8}
61+
var output []int
62+
63+
{
64+
err := godash.Filter(input, &output, func(a int) int {
65+
return a
66+
})
67+
assert.EqualError(t, err, "predicate function should return only a (boolean) and not a (int)")
68+
}
69+
{
70+
err := godash.Filter(input, &output, func(int) (int, bool) {
71+
return 1, true
72+
})
73+
assert.EqualError(t, err, "predicate function should return only one return value - a boolean")
74+
}
75+
})
76+
77+
t.Run("should validate output's type", func(t *testing.T) {
78+
input := []int{1, 2, 3, 4, 5, 6, 7, 8}
79+
var output []string
80+
81+
err := godash.Filter(input, &output, func(a int) bool {
82+
return a == 0
83+
})
84+
85+
assert.EqualError(t, err, "input([]int) and output([]string) should be of the same Type")
86+
})
87+
88+
t.Run("should not panic if output is nil", func(t *testing.T) {
89+
in := []int{1, 2, 3}
90+
{
91+
var out []int
92+
93+
err := godash.Filter(in, out, func(int) bool { return true })
94+
95+
assert.EqualError(t, err, "output is nil. Pass a reference to set output")
96+
}
97+
{
98+
err := godash.Filter(in, nil, func(int) bool { return true })
99+
100+
assert.EqualError(t, err, "output is nil. Pass a reference to set output")
101+
}
102+
})
103+
}

map.go

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
func Map(in, out, mapperFn interface{}) error {
1010
input := reflect.ValueOf(in)
1111
output := reflect.ValueOf(out)
12-
if err := validateMapperOut(output); err != nil {
12+
if err := validateOut(output); err != nil {
1313
return err
1414
}
1515

@@ -38,22 +38,6 @@ func Map(in, out, mapperFn interface{}) error {
3838
return fmt.Errorf("not implemented")
3939
}
4040

41-
func validateMapperOut(output reflect.Value) error {
42-
zeroValue := reflect.Value{}
43-
if output == zeroValue {
44-
return fmt.Errorf("output is nil. Pass a reference to set output")
45-
}
46-
47-
if output.IsNil() {
48-
return fmt.Errorf("output is nil. Pass a reference to set output")
49-
}
50-
51-
if !output.Elem().CanSet() {
52-
return fmt.Errorf("cannot set out. Pass a reference to set output")
53-
}
54-
55-
return nil
56-
}
5741

5842
func validateMapperFunction(mapper reflect.Value) error {
5943
if mapper.Kind() != reflect.Func {

0 commit comments

Comments
 (0)