...
1 package funk
2
3 import (
4 "reflect"
5 )
6
7
8
9 func Reduce(arr, reduceFunc, acc interface{}) interface{} {
10 arrValue := redirectValue(reflect.ValueOf(arr))
11
12 if !IsIteratee(arrValue.Interface()) {
13 panic("First parameter must be an iteratee")
14 }
15
16 returnType := reflect.TypeOf(Reduce).Out(0)
17
18 isFunc := IsFunction(reduceFunc, 2, 1)
19 isRune := reflect.TypeOf(reduceFunc).Kind() == reflect.Int32
20
21 if !(isFunc || isRune) {
22 panic("Second argument must be a valid function or rune")
23 }
24
25 accValue := reflect.ValueOf(acc)
26 sliceElemType := sliceElem(arrValue.Type())
27
28 if isRune {
29 if arrValue.Kind() == reflect.Slice && sliceElemType.Kind() == reflect.Interface {
30 accValue = accValue.Convert(returnType)
31 } else {
32 accValue = accValue.Convert(sliceElemType)
33 }
34 } else {
35 accValue = accValue.Convert(reflect.TypeOf(reduceFunc).In(0))
36 }
37
38 accType := accValue.Type()
39
40
41 if isRune {
42 reduceSign := reduceFunc.(int32)
43
44 if ok := map[rune]bool{'+': true, '*': true}[reduceSign]; !ok {
45 panic("Invalid reduce sign, allowed: '+' and '*'")
46 }
47
48 in := []reflect.Type{accType, sliceElemType}
49 out := []reflect.Type{accType}
50 funcType := reflect.FuncOf(in, out, false)
51
52 reduceFunc = reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value {
53 acc := args[0].Interface()
54 elem := args[1].Interface()
55
56 var result float64
57 params := []interface{}{acc, elem}
58 switch reduceSign {
59 case '+':
60 result = Sum(params)
61 case '*':
62 result = Product(params)
63 }
64
65 return []reflect.Value{reflect.ValueOf(result).Convert(accType)}
66 }).Interface()
67 }
68
69 funcValue := reflect.ValueOf(reduceFunc)
70 funcType := funcValue.Type()
71
72 for i := 0; i < arrValue.Len(); i++ {
73 if accType.ConvertibleTo(funcType.In(0)) {
74 accValue = accValue.Convert(funcType.In(0))
75 }
76
77 arrElementValue := arrValue.Index(i)
78 if sliceElemType.ConvertibleTo(funcType.In(1)) {
79 arrElementValue = arrElementValue.Convert(funcType.In(1))
80 }
81
82 result := funcValue.Call([]reflect.Value{accValue, arrElementValue})
83 accValue = result[0]
84 }
85
86 return accValue.Convert(returnType).Interface()
87 }
88
View as plain text