...

Source file src/github.com/thoas/go-funk/reduce.go

Documentation: github.com/thoas/go-funk

     1  package funk
     2  
     3  import (
     4  	"reflect"
     5  )
     6  
     7  // Reduce takes a collection and reduces it to a single value using a reduction
     8  // function (or a valid symbol) and an accumulator value.
     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  	// Generate reduce function if was passed as rune
    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