1package funk
2
3import (
4	"fmt"
5	"reflect"
6	"strings"
7)
8
9// Filter iterates over elements of collection, returning an array of
10// all elements predicate returns truthy for.
11func Filter(arr interface{}, predicate interface{}) interface{} {
12	if !IsIteratee(arr) {
13		panic("First parameter must be an iteratee")
14	}
15
16	if !IsFunction(predicate, 1, 1) {
17		panic("Second argument must be function")
18	}
19
20	funcValue := reflect.ValueOf(predicate)
21
22	funcType := funcValue.Type()
23
24	if funcType.Out(0).Kind() != reflect.Bool {
25		panic("Return argument should be a boolean")
26	}
27
28	arrValue := reflect.ValueOf(arr)
29
30	arrType := arrValue.Type()
31
32	// Get slice type corresponding to array type
33	resultSliceType := reflect.SliceOf(arrType.Elem())
34
35	// MakeSlice takes a slice kind type, and makes a slice.
36	resultSlice := reflect.MakeSlice(resultSliceType, 0, 0)
37
38	for i := 0; i < arrValue.Len(); i++ {
39		elem := arrValue.Index(i)
40
41		result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool)
42
43		if result {
44			resultSlice = reflect.Append(resultSlice, elem)
45		}
46	}
47
48	return resultSlice.Interface()
49}
50
51// Find iterates over elements of collection, returning the first
52// element predicate returns truthy for.
53func Find(arr interface{}, predicate interface{}) interface{} {
54	_, val := FindKey(arr, predicate)
55	return val
56}
57
58// Find iterates over elements of collection, returning the first
59// element of an array and random of a map which predicate returns truthy for.
60func FindKey(arr interface{}, predicate interface{}) (matchKey, matchEle interface{}) {
61	if !IsIteratee(arr) {
62		panic("First parameter must be an iteratee")
63	}
64
65	if !IsFunction(predicate, 1, 1) {
66		panic("Second argument must be function")
67	}
68
69	funcValue := reflect.ValueOf(predicate)
70
71	funcType := funcValue.Type()
72
73	if funcType.Out(0).Kind() != reflect.Bool {
74		panic("Return argument should be a boolean")
75	}
76
77	arrValue := reflect.ValueOf(arr)
78	var keyArrs []reflect.Value
79
80	isMap := arrValue.Kind() == reflect.Map
81	if isMap {
82		keyArrs = arrValue.MapKeys()
83	}
84	for i := 0; i < arrValue.Len(); i++ {
85		var (
86			elem reflect.Value
87			key  reflect.Value
88		)
89		if isMap {
90			key = keyArrs[i]
91			elem = arrValue.MapIndex(key)
92		} else {
93			key = reflect.ValueOf(i)
94			elem = arrValue.Index(i)
95		}
96
97		result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool)
98
99		if result {
100			return key.Interface(), elem.Interface()
101		}
102	}
103
104	return nil, nil
105}
106
107// IndexOf gets the index at which the first occurrence of value is found in array or return -1
108// if the value cannot be found
109func IndexOf(in interface{}, elem interface{}) int {
110	inValue := reflect.ValueOf(in)
111
112	elemValue := reflect.ValueOf(elem)
113
114	inType := inValue.Type()
115
116	if inType.Kind() == reflect.String {
117		return strings.Index(inValue.String(), elemValue.String())
118	}
119
120	if inType.Kind() == reflect.Slice {
121		equalTo := equal(elem)
122		for i := 0; i < inValue.Len(); i++ {
123			if equalTo(reflect.Value{}, inValue.Index(i)) {
124				return i
125			}
126		}
127	}
128
129	return -1
130}
131
132// LastIndexOf gets the index at which the last occurrence of value is found in array or return -1
133// if the value cannot be found
134func LastIndexOf(in interface{}, elem interface{}) int {
135	inValue := reflect.ValueOf(in)
136
137	elemValue := reflect.ValueOf(elem)
138
139	inType := inValue.Type()
140
141	if inType.Kind() == reflect.String {
142		return strings.LastIndex(inValue.String(), elemValue.String())
143	}
144
145	if inType.Kind() == reflect.Slice {
146		length := inValue.Len()
147
148		equalTo := equal(elem)
149		for i := length - 1; i >= 0; i-- {
150			if equalTo(reflect.Value{}, inValue.Index(i)) {
151				return i
152			}
153		}
154	}
155
156	return -1
157}
158
159// Contains returns true if an element is present in a iteratee.
160func Contains(in interface{}, elem interface{}) bool {
161	inValue := reflect.ValueOf(in)
162	elemValue := reflect.ValueOf(elem)
163	inType := inValue.Type()
164
165	switch inType.Kind() {
166	case reflect.String:
167		return strings.Contains(inValue.String(), elemValue.String())
168	case reflect.Map:
169		equalTo := equal(elem, true)
170		for _, key := range inValue.MapKeys() {
171			if equalTo(key, inValue.MapIndex(key)) {
172				return true
173			}
174		}
175	case reflect.Slice, reflect.Array:
176		equalTo := equal(elem)
177		for i := 0; i < inValue.Len(); i++ {
178			if equalTo(reflect.Value{}, inValue.Index(i)) {
179				return true
180			}
181		}
182	default:
183		panic(fmt.Sprintf("Type %s is not supported by Contains, supported types are String, Map, Slice, Array", inType.String()))
184	}
185
186	return false
187}
188
189// Every returns true if every element is present in a iteratee.
190func Every(in interface{}, elements ...interface{}) bool {
191	for _, elem := range elements {
192		if !Contains(in, elem) {
193			return false
194		}
195	}
196	return true
197}
198
199// Some returns true if atleast one element is present in an iteratee.
200func Some(in interface{}, elements ...interface{}) bool {
201	for _, elem := range elements {
202		if Contains(in, elem) {
203			return true
204		}
205	}
206	return false
207}
208