1package funk
2
3import (
4	"fmt"
5	"math/rand"
6	"reflect"
7	"strings"
8)
9
10// Chunk creates an array of elements split into groups with the length of size.
11// If array can't be split evenly, the final chunk will be
12// the remaining element.
13func Chunk(arr interface{}, size int) interface{} {
14	if !IsIteratee(arr) {
15		panic("First parameter must be neither array nor slice")
16	}
17
18	if size == 0 {
19		return arr
20	}
21
22	arrValue := reflect.ValueOf(arr)
23
24	arrType := arrValue.Type()
25
26	resultSliceType := reflect.SliceOf(arrType)
27
28	// Initialize final result slice which will contains slice
29	resultSlice := reflect.MakeSlice(resultSliceType, 0, 0)
30
31	itemType := arrType.Elem()
32
33	var itemSlice reflect.Value
34
35	itemSliceType := reflect.SliceOf(itemType)
36
37	length := arrValue.Len()
38
39	for i := 0; i < length; i++ {
40		if i%size == 0 || i == 0 {
41			if itemSlice.Kind() != reflect.Invalid {
42				resultSlice = reflect.Append(resultSlice, itemSlice)
43			}
44
45			itemSlice = reflect.MakeSlice(itemSliceType, 0, 0)
46		}
47
48		itemSlice = reflect.Append(itemSlice, arrValue.Index(i))
49
50		if i == length-1 {
51			resultSlice = reflect.Append(resultSlice, itemSlice)
52		}
53	}
54
55	return resultSlice.Interface()
56}
57
58// ToMap transforms a slice of instances to a Map.
59// []*Foo => Map<int, *Foo>
60func ToMap(in interface{}, pivot string) interface{} {
61	value := reflect.ValueOf(in)
62
63	// input value must be a slice
64	if value.Kind() != reflect.Slice {
65		panic(fmt.Sprintf("%v must be a slice", in))
66	}
67
68	inType := value.Type()
69
70	structType := inType.Elem()
71
72	// retrieve the struct in the slice to deduce key type
73	if structType.Kind() == reflect.Ptr {
74		structType = structType.Elem()
75	}
76
77	field, _ := structType.FieldByName(pivot)
78
79	// value of the map will be the input type
80	collectionType := reflect.MapOf(field.Type, inType.Elem())
81
82	// create a map from scratch
83	collection := reflect.MakeMap(collectionType)
84
85	for i := 0; i < value.Len(); i++ {
86		instance := value.Index(i)
87		var field reflect.Value
88
89		if instance.Kind() == reflect.Ptr {
90			field = instance.Elem().FieldByName(pivot)
91		} else {
92			field = instance.FieldByName(pivot)
93		}
94
95		collection.SetMapIndex(field, instance)
96	}
97
98	return collection.Interface()
99}
100
101func mapSlice(arrValue reflect.Value, funcValue reflect.Value) reflect.Value {
102	funcType := funcValue.Type()
103
104	if funcType.NumIn() != 1 || funcType.NumOut() == 0 || funcType.NumOut() > 2 {
105		panic("Map function with an array must have one parameter and must return one or two parameters")
106	}
107
108	arrElemType := arrValue.Type().Elem()
109
110	// Checking whether element type is convertible to function's first argument's type.
111	if !arrElemType.ConvertibleTo(funcType.In(0)) {
112		panic("Map function's argument is not compatible with type of array.")
113	}
114
115	if funcType.NumOut() == 1 {
116		// Get slice type corresponding to function's return value's type.
117		resultSliceType := reflect.SliceOf(funcType.Out(0))
118
119		// MakeSlice takes a slice kind type, and makes a slice.
120		resultSlice := reflect.MakeSlice(resultSliceType, 0, 0)
121
122		for i := 0; i < arrValue.Len(); i++ {
123			result := funcValue.Call([]reflect.Value{arrValue.Index(i)})[0]
124
125			resultSlice = reflect.Append(resultSlice, result)
126		}
127
128		return resultSlice
129	}
130
131	if funcType.NumOut() == 2 {
132		// value of the map will be the input type
133		collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1))
134
135		// create a map from scratch
136		collection := reflect.MakeMap(collectionType)
137
138		for i := 0; i < arrValue.Len(); i++ {
139			results := funcValue.Call([]reflect.Value{arrValue.Index(i)})
140
141			collection.SetMapIndex(results[0], results[1])
142		}
143
144		return collection
145	}
146
147	return reflect.Value{}
148}
149
150func mapMap(arrValue reflect.Value, funcValue reflect.Value) reflect.Value {
151	funcType := funcValue.Type()
152
153	if funcType.NumIn() != 2 || funcType.NumOut() == 0 || funcType.NumOut() > 2 {
154		panic("Map function with a map must have two parameters and must return one or two parameters")
155	}
156
157	// Only one returned parameter, should be a slice
158	if funcType.NumOut() == 1 {
159		// Get slice type corresponding to function's return value's type.
160		resultSliceType := reflect.SliceOf(funcType.Out(0))
161
162		// MakeSlice takes a slice kind type, and makes a slice.
163		resultSlice := reflect.MakeSlice(resultSliceType, 0, 0)
164
165		for _, key := range arrValue.MapKeys() {
166			results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)})
167
168			result := results[0]
169
170			resultSlice = reflect.Append(resultSlice, result)
171		}
172
173		return resultSlice
174	}
175
176	// two parameters, should be a map
177	if funcType.NumOut() == 2 {
178		// value of the map will be the input type
179		collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1))
180
181		// create a map from scratch
182		collection := reflect.MakeMap(collectionType)
183
184		for _, key := range arrValue.MapKeys() {
185			results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)})
186
187			collection.SetMapIndex(results[0], results[1])
188
189		}
190
191		return collection
192	}
193
194	return reflect.Value{}
195}
196
197// Map manipulates an iteratee and transforms it to another type.
198func Map(arr interface{}, mapFunc interface{}) interface{} {
199	result := mapFn(arr, mapFunc, "Map")
200
201	if result.IsValid() {
202		return result.Interface()
203	}
204
205	return nil
206}
207
208func mapFn(arr interface{}, mapFunc interface{}, funcName string) reflect.Value {
209	if !IsIteratee(arr) {
210		panic("First parameter must be an iteratee")
211	}
212
213	if !IsFunction(mapFunc) {
214		panic("Second argument must be function")
215	}
216
217	var (
218		funcValue = reflect.ValueOf(mapFunc)
219		arrValue  = reflect.ValueOf(arr)
220		arrType   = arrValue.Type()
221	)
222
223	kind := arrType.Kind()
224
225	if kind == reflect.Slice || kind == reflect.Array {
226		return mapSlice(arrValue, funcValue)
227	} else if kind == reflect.Map {
228		return mapMap(arrValue, funcValue)
229	}
230
231	panic(fmt.Sprintf("Type %s is not supported by "+funcName, arrType.String()))
232}
233
234// FlatMap manipulates an iteratee and transforms it to a flattened collection of another type.
235func FlatMap(arr interface{}, mapFunc interface{}) interface{} {
236	result := mapFn(arr, mapFunc, "FlatMap")
237
238	if result.IsValid() {
239		return flatten(result).Interface()
240	}
241
242	return nil
243}
244
245// Flatten flattens a two-dimensional array.
246func Flatten(out interface{}) interface{} {
247	return flatten(reflect.ValueOf(out)).Interface()
248}
249
250func flatten(value reflect.Value) reflect.Value {
251	sliceType := value.Type()
252
253	if (value.Kind() != reflect.Slice && value.Kind() != reflect.Array) ||
254		(sliceType.Elem().Kind() != reflect.Slice && sliceType.Elem().Kind() != reflect.Array) {
255		panic("Argument must be an array or slice of at least two dimensions")
256	}
257
258	resultSliceType := sliceType.Elem().Elem()
259
260	resultSlice := reflect.MakeSlice(reflect.SliceOf(resultSliceType), 0, 0)
261
262	length := value.Len()
263
264	for i := 0; i < length; i++ {
265		item := value.Index(i)
266
267		resultSlice = reflect.AppendSlice(resultSlice, item)
268	}
269
270	return resultSlice
271}
272
273// FlattenDeep recursively flattens array.
274func FlattenDeep(out interface{}) interface{} {
275	return flattenDeep(reflect.ValueOf(out)).Interface()
276}
277
278func flattenDeep(value reflect.Value) reflect.Value {
279	sliceType := sliceElem(value.Type())
280
281	resultSlice := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, 0)
282
283	return flattenRecursive(value, resultSlice)
284}
285
286func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value {
287	length := value.Len()
288
289	for i := 0; i < length; i++ {
290		item := value.Index(i)
291		kind := item.Kind()
292
293		if kind == reflect.Slice || kind == reflect.Array {
294			result = flattenRecursive(item, result)
295		} else {
296			result = reflect.Append(result, item)
297		}
298	}
299
300	return result
301}
302
303// Shuffle creates an array of shuffled values
304func Shuffle(in interface{}) interface{} {
305	value := reflect.ValueOf(in)
306	valueType := value.Type()
307
308	kind := value.Kind()
309
310	if kind == reflect.Array || kind == reflect.Slice {
311		length := value.Len()
312
313		resultSlice := makeSlice(value, length)
314
315		for i, v := range rand.Perm(length) {
316			resultSlice.Index(i).Set(value.Index(v))
317		}
318
319		return resultSlice.Interface()
320	}
321
322	panic(fmt.Sprintf("Type %s is not supported by Shuffle", valueType.String()))
323}
324
325// Reverse transforms an array the first element will become the last,
326// the second element will become the second to last, etc.
327func Reverse(in interface{}) interface{} {
328	value := reflect.ValueOf(in)
329	valueType := value.Type()
330
331	kind := value.Kind()
332
333	if kind == reflect.String {
334		return ReverseString(in.(string))
335	}
336
337	if kind == reflect.Array || kind == reflect.Slice {
338		length := value.Len()
339
340		resultSlice := makeSlice(value, length)
341
342		j := 0
343		for i := length - 1; i >= 0; i-- {
344			resultSlice.Index(j).Set(value.Index(i))
345			j++
346		}
347
348		return resultSlice.Interface()
349	}
350
351	panic(fmt.Sprintf("Type %s is not supported by Reverse", valueType.String()))
352}
353
354// Uniq creates an array with unique values.
355func Uniq(in interface{}) interface{} {
356	value := reflect.ValueOf(in)
357	valueType := value.Type()
358
359	kind := value.Kind()
360
361	if kind == reflect.Array || kind == reflect.Slice {
362		length := value.Len()
363
364		result := makeSlice(value, 0)
365
366		seen := make(map[interface{}]bool, length)
367		j := 0
368
369		for i := 0; i < length; i++ {
370			val := value.Index(i)
371			v := val.Interface()
372
373			if _, ok := seen[v]; ok {
374				continue
375			}
376
377			seen[v] = true
378			result = reflect.Append(result, val)
379			j++
380		}
381
382		return result.Interface()
383	}
384
385	panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String()))
386}
387
388// ConvertSlice converts a slice type to another,
389// a perfect example would be to convert a slice of struct to a slice of interface.
390func ConvertSlice(in interface{}, out interface{}) {
391	srcValue := reflect.ValueOf(in)
392
393	dstValue := reflect.ValueOf(out)
394
395	if dstValue.Kind() != reflect.Ptr {
396		panic("Second argument must be a pointer")
397	}
398
399	dstValue = dstValue.Elem()
400
401	if srcValue.Kind() != reflect.Slice && srcValue.Kind() != reflect.Array {
402		panic("First argument must be an array or slice")
403	}
404
405	if dstValue.Kind() != reflect.Slice && dstValue.Kind() != reflect.Array {
406		panic("Second argument must be an array or slice")
407	}
408
409	// returns value that points to dstValue
410	direct := reflect.Indirect(dstValue)
411
412	length := srcValue.Len()
413
414	for i := 0; i < length; i++ {
415		dstValue = reflect.Append(dstValue, srcValue.Index(i))
416	}
417
418	direct.Set(dstValue)
419}
420
421// Drop creates an array/slice with `n` elements dropped from the beginning.
422func Drop(in interface{}, n int) interface{} {
423	value := reflect.ValueOf(in)
424	valueType := value.Type()
425
426	kind := value.Kind()
427
428	if kind == reflect.Array || kind == reflect.Slice {
429		length := value.Len()
430
431		resultSlice := makeSlice(value, length-n)
432
433		j := 0
434		for i := n; i < length; i++ {
435			resultSlice.Index(j).Set(value.Index(i))
436			j++
437		}
438
439		return resultSlice.Interface()
440
441	}
442
443	panic(fmt.Sprintf("Type %s is not supported by Drop", valueType.String()))
444}
445
446// Prune returns a copy of "in" that only contains fields in "paths"
447// which are looked up using struct field name.
448// For lookup paths by field tag instead, use funk.PruneByTag()
449func Prune(in interface{}, paths []string) (interface{}, error) {
450	return pruneByTag(in, paths, nil /*tag*/)
451}
452
453// pruneByTag returns a copy of "in" that only contains fields in "paths"
454// which are looked up using struct field Tag "tag".
455func PruneByTag(in interface{}, paths []string, tag string) (interface{}, error) {
456	return pruneByTag(in, paths, &tag)
457}
458
459// pruneByTag returns a copy of "in" that only contains fields in "paths"
460// which are looked up using struct field Tag "tag". If tag is nil,
461// traverse paths using struct field name
462func pruneByTag(in interface{}, paths []string, tag *string) (interface{}, error) {
463
464	inValue := reflect.ValueOf(in)
465
466	ret := reflect.New(inValue.Type()).Elem()
467
468	for _, path := range paths {
469		parts := strings.Split(path, ".")
470		if err := prune(inValue, ret, parts, tag); err != nil {
471			return nil, err
472		}
473	}
474	return ret.Interface(), nil
475}
476
477func prune(inValue reflect.Value, ret reflect.Value, parts []string, tag *string) error {
478
479	if len(parts) == 0 {
480		// we reached the location that ret needs to hold inValue
481		// Note: The value at the end of the path is not copied, maybe we need to change.
482		// ret and the original data holds the same reference to this value
483		ret.Set(inValue)
484		return nil
485	}
486
487	inKind := inValue.Kind()
488
489	switch inKind {
490	case reflect.Ptr:
491		if inValue.IsNil() {
492			// TODO validate
493			return nil
494		}
495		if ret.IsNil() {
496			// init ret and go to next level
497			ret.Set(reflect.New(inValue.Type().Elem()))
498		}
499		return prune(inValue.Elem(), ret.Elem(), parts, tag)
500	case reflect.Struct:
501		part := parts[0]
502		var fValue reflect.Value
503		var fRet reflect.Value
504		if tag == nil {
505			// use field name
506			fValue = inValue.FieldByName(part)
507			if !fValue.IsValid() {
508				return fmt.Errorf("field name %v is not found in struct %v", part, inValue.Type().String())
509			}
510			fRet = ret.FieldByName(part)
511		} else {
512			// search tag that has key equal to part
513			found := false
514			for i := 0; i < inValue.NumField(); i++ {
515				f := inValue.Type().Field(i)
516				if key, ok := f.Tag.Lookup(*tag); ok {
517					if key == part {
518						fValue = inValue.Field(i)
519						fRet = ret.Field(i)
520						found = true
521						break
522					}
523				}
524			}
525			if !found {
526				return fmt.Errorf("Struct tag %v is not found with key %v", *tag, part)
527			}
528		}
529		// init Ret is zero and go down one more level
530		if fRet.IsZero() {
531			fRet.Set(reflect.New(fValue.Type()).Elem())
532		}
533		return prune(fValue, fRet, parts[1:], tag)
534	case reflect.Array, reflect.Slice:
535		// set all its elements
536		length := inValue.Len()
537		// init ret
538		if ret.IsZero() {
539			if inKind == reflect.Slice {
540				ret.Set(reflect.MakeSlice(inValue.Type(), length /*len*/, length /*cap*/))
541			} else { // array
542				ret.Set(reflect.New(inValue.Type()).Elem())
543			}
544		}
545		for j := 0; j < length; j++ {
546			if err := prune(inValue.Index(j), ret.Index(j), parts, tag); err != nil {
547				return err
548			}
549		}
550	default:
551		return fmt.Errorf("path %v cannot be looked up on kind of %v", strings.Join(parts, "."), inValue.Kind())
552	}
553
554	return nil
555}
556