Skip to content

Commit 496735a

Browse files
authored
Merge pull request #15 from pearl2201/master
Add All and Every function
2 parents a67789f + c0d3bf1 commit 496735a

File tree

3 files changed

+245
-5
lines changed

3 files changed

+245
-5
lines changed

README.md

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Inspired from [Lodash](https://github.com/lodash/lodash) for golang
2020
3. [Reduce](#Reduce)
2121
4. [Any](#Any-or-Some) or [Some](#Any-or-Some)
2222
5. [Find](#Find)
23+
6. [All](#All-or-Every) or [Every](#All-or-Every)
2324

2425
## Usages
2526

@@ -150,15 +151,14 @@ func main() {
150151
Any or Some checks if predicate returns truthy for any element of collection. Iteration is stopped once predicate returns truthy.
151152
For more [docs](https://godoc.org/github.com/thecasualcoder/godash#Any).
152153

154+
153155
```go
154156
func main() {
155157
input := []int{1, 2, 3, 4, 5}
156158
var output []int
157-
158159
output, _ := godash.Any(input, func(num int) bool {
159160
return num % 7 == 0
160161
})
161-
162162
fmt.Println(output) // prints false
163163
}
164164
```
@@ -170,15 +170,14 @@ func main() {
170170
{Name: "Doe", Age: 15},
171171
}
172172
var output int
173-
174173
output, _ := godash.Some(input, func(person Person) bool {
175174
return person.Age < 18
176175
})
177-
178176
fmt.Println(output) // prints true
179177
}
180178
```
181179

180+
182181
### Find
183182

184183
Returns the first element which passes the predicate.
@@ -194,4 +193,33 @@ func main() {
194193
// output is "wick"
195194
fmt.Println(output)
196195
}
197-
```
196+
```
197+
198+
### All or Every
199+
200+
All or Every checks if predicate returns truthy for all element of collection. Iteration is stopped once predicate returns falsely.
201+
For more [docs](https://godoc.org/github.com/thecasualcoder/godash#All).
202+
203+
```go
204+
func main() {
205+
input := []int{1, 2, 3, 4, 5}
206+
var output bool
207+
output, _ := godash.All(input, func(num int) bool {
208+
return num >= 1
209+
})
210+
fmt.Println(output) // prints true
211+
}
212+
```
213+
214+
```go
215+
func main() {
216+
input := []Person{
217+
{Name: "John", Age: 25},
218+
{Name: "Doe", Age: 15},
219+
}
220+
var output bool
221+
output, _ := godash.Every(input, func(person Person) bool {
222+
return person.Age < 18
223+
})
224+
fmt.Println(output) // prints false
225+
}

all.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package godash
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
// All checks if predicate returns truthy for all element of collection. Iteration is stopped once predicate returns falsely.
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 All(in, predicateFn interface{}) (bool, error) {
19+
20+
input := reflect.ValueOf(in)
21+
predicate := reflect.ValueOf(predicateFn)
22+
23+
if predicate.Kind() != reflect.Func {
24+
return false, fmt.Errorf("predicateFn has to be a function")
25+
}
26+
27+
predicateFnType := predicate.Type()
28+
if predicateFnType.NumIn() != 1 {
29+
return false, fmt.Errorf("predicate function has to take only one argument")
30+
}
31+
32+
if predicateFnType.NumOut() != 1 {
33+
return false, fmt.Errorf("predicate function should return only one return value")
34+
}
35+
36+
if predicateFnType.Out(0).Kind() != reflect.Bool {
37+
return false, 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 false, 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 false, nil
53+
}
54+
}
55+
56+
return true, nil
57+
}
58+
59+
return false, fmt.Errorf("not implemented for (%s)", inputKind)
60+
}
61+
62+
// Every is an alias for All function
63+
func Every(in, predicateFn interface{}) (bool, error) {
64+
return All(in, predicateFn)
65+
}

all_test.go

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

0 commit comments

Comments
 (0)