Skip to content

Commit 45b2e91

Browse files
arunvelsriramdineshba
authored andcommitted
Implement "Any" function
1 parent d86cbea commit 45b2e91

File tree

3 files changed

+223
-0
lines changed

3 files changed

+223
-0
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This library heavily makes use of `reflect` package and hence will have an **imp
1717
1. [Map](#Map)
1818
2. [Filter](#Filter)
1919
3. [Reduce](#Reduce)
20+
4. [Any](#Any)
2021

2122
## Usages
2223

@@ -141,3 +142,37 @@ func main() {
141142
fmt.Println(output) // prints 45
142143
}
143144
```
145+
146+
### Any
147+
148+
Any checks if predicate returns truthy for any element of collection. Iteration is stopped once predicate returns truthy.
149+
For more [docs](https://godoc.org/github.com/thecasualcoder/godash#Any).
150+
151+
```go
152+
func main() {
153+
input := []int{1, 2, 3, 4, 5}
154+
var output []int
155+
156+
output, _ := godash.Any(input, func(num int) bool {
157+
return num % 7 == 0
158+
})
159+
160+
fmt.Println(output) // prints false
161+
}
162+
```
163+
164+
```go
165+
func main() {
166+
input := []Person{
167+
{Name: "John", Age: 25},
168+
{Name: "Doe", Age: 15},
169+
}
170+
var output int
171+
172+
output, _ := godash.Any(input, func(person Person) bool {
173+
return person.Age < 18
174+
})
175+
176+
fmt.Println(output) // prints true
177+
}
178+
```

any.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package godash
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
// Any checks if predicate returns truthy for any element of collection. Iteration is stopped once predicate returns truthy.
9+
// Currently, input of type slice is supported
10+
//
11+
// Validations:
12+
//
13+
// 1. Predicate function should take one argument and return one value
14+
// 2. Predicate function should return a bool value
15+
// 3. Predicate function's argument should be of the same type as the elements of the input slice
16+
//
17+
// Validation errors are returned to the caller
18+
func Any(in, predicateFn interface{}) (bool, error) {
19+
var output bool
20+
input := reflect.ValueOf(in)
21+
predicate := reflect.ValueOf(predicateFn)
22+
23+
if predicate.Kind() != reflect.Func {
24+
return output, fmt.Errorf("predicateFn has to be a function")
25+
}
26+
27+
predicateFnType := predicate.Type()
28+
if predicateFnType.NumIn() != 1 {
29+
return output, fmt.Errorf("predicate function has to take only one argument")
30+
}
31+
32+
if predicateFnType.NumOut() != 1 {
33+
return output, fmt.Errorf("predicate function should return only one return value")
34+
}
35+
36+
if predicateFnType.Out(0).Kind() != reflect.Bool {
37+
return output, fmt.Errorf("predicate function should return a boolean value")
38+
}
39+
40+
inputKind := input.Kind()
41+
if inputKind == reflect.Slice {
42+
inputSliceElemType := input.Type().Elem
43+
predicateFnArgType := predicateFnType.In(0)
44+
if inputSliceElemType() != predicateFnArgType {
45+
return output, fmt.Errorf("predicate function's argument (%s) has to be (%s)", predicateFnArgType, inputSliceElemType())
46+
}
47+
48+
for i := 0; i < input.Len(); i++ {
49+
arg := input.Index(i)
50+
returnValue := predicate.Call([]reflect.Value{arg})[0]
51+
if returnValue.Bool() {
52+
return true, nil
53+
}
54+
}
55+
56+
return output, nil
57+
}
58+
59+
return output, fmt.Errorf("not implemented for (%s)", inputKind)
60+
}

any_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package godash_test
2+
3+
import (
4+
"fmt"
5+
"github.com/stretchr/testify/assert"
6+
"github.com/thecasualcoder/godash"
7+
"testing"
8+
)
9+
10+
func TestAny(t *testing.T) {
11+
12+
t.Run("should return err if predicate is not a function", func(t *testing.T) {
13+
in := []int{1, 2, 3}
14+
15+
_, err := godash.Any(in, "not a func")
16+
17+
assert.EqualError(t, err, "predicateFn has to be a function")
18+
})
19+
20+
t.Run("should return err if predicate function do not take exactly one argument", func(t *testing.T) {
21+
in := []int{1, 2, 3}
22+
23+
{
24+
_, err := godash.Any(in, func() {})
25+
26+
assert.EqualError(t, err, "predicate function has to take only one argument")
27+
}
28+
{
29+
_, err := godash.Any(in, func(int, int) {})
30+
31+
assert.EqualError(t, err, "predicate function has to take only one argument")
32+
}
33+
})
34+
35+
t.Run("should return err if predicate function do not return exactly one value", func(t *testing.T) {
36+
in := []int{1, 2, 3}
37+
38+
{
39+
_, err := godash.Any(in, func(int) {})
40+
41+
assert.EqualError(t, err, "predicate function should return only one return value")
42+
}
43+
{
44+
_, err := godash.Any(in, func(int) (bool, bool) { return true, true })
45+
46+
assert.EqualError(t, err, "predicate function should return only one return value")
47+
48+
}
49+
})
50+
51+
t.Run("should return err if predicate function's return value is not a boolean", func(t *testing.T) {
52+
in := []int{1, 2, 3}
53+
54+
_, err := godash.Any(in, func(int) int { return 0 })
55+
56+
assert.EqualError(t, err, "predicate function should return a boolean value")
57+
})
58+
59+
t.Run("should return err if input is not a slice", func(t *testing.T) {
60+
in := 1
61+
62+
_, err := godash.Any(in, func(int) bool { return true })
63+
64+
assert.EqualError(t, err, "not implemented for (int)")
65+
})
66+
67+
t.Run("should return err if there is a type mismatch between predicate function's argument and input slice", func(t *testing.T) {
68+
in := []string{"hello", "world"}
69+
70+
_, err := godash.Any(in, func(int) bool { return true })
71+
72+
assert.EqualError(t, err, "predicate function's argument (int) has to be (string)")
73+
})
74+
75+
t.Run("should return true if predicate passes for any of the element in input slice", func(t *testing.T) {
76+
in := []int{1, 1, 2, 3, 5, 8, 13}
77+
78+
output, err := godash.Any(in, func(elem int) bool { return elem%5 == 0 })
79+
80+
assert.NoError(t, err)
81+
assert.True(t, output)
82+
})
83+
84+
t.Run("should return false if predicate fails for all the elements in input slice", func(t *testing.T) {
85+
in := []int{1, 1, 2, 3, 5, 8, 13}
86+
87+
output, err := godash.Any(in, func(num int) bool { return num%6 == 0 })
88+
89+
assert.NoError(t, err)
90+
assert.False(t, output)
91+
})
92+
93+
t.Run("should support structs", func(t *testing.T) {
94+
type person struct {
95+
name string
96+
age int
97+
}
98+
in := []person{
99+
{name: "John", age: 12},
100+
{name: "Doe", age: 25},
101+
}
102+
103+
{
104+
output, err := godash.Any(in, func(person person) bool { return person.age < 18 })
105+
106+
assert.NoError(t, err)
107+
assert.True(t, output)
108+
}
109+
{
110+
output, err := godash.Any(in, func(person person) bool { return person.age < 30 })
111+
112+
assert.NoError(t, err)
113+
assert.True(t, output)
114+
}
115+
})
116+
}
117+
118+
func ExampleAny() {
119+
input := []int{0, 1, 2, 3, 4}
120+
121+
output, _ := godash.Any(input, func(num int) bool {
122+
return num%3 == 0
123+
})
124+
125+
fmt.Println(output)
126+
127+
// Output: true
128+
}

0 commit comments

Comments
 (0)