diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index b025c7670..5d2cf3b35 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -545,7 +545,7 @@ func (ns *Namespace) Slice(args ...interface{}) []interface{} { // If either l1 or l2 is nil then the non-nil list will be returned. func (ns *Namespace) Union(l1, l2 interface{}) (interface{}, error) { if l1 == nil && l2 == nil { - return nil, errors.New("both arrays/slices have to be of the same type") + return []interface{}{}, nil } else if l1 == nil && l2 != nil { return l2, nil } else if l1 != nil && l2 == nil { @@ -561,21 +561,96 @@ func (ns *Namespace) Union(l1, l2 interface{}) (interface{}, error) { case reflect.Array, reflect.Slice: r := reflect.MakeSlice(l1v.Type(), 0, 0) - if l1v.Type() != l2v.Type() { + if l1v.Type() != l2v.Type() && + l1v.Type().Elem().Kind() != reflect.Interface && + l2v.Type().Elem().Kind() != reflect.Interface { + return r.Interface(), nil } + var l1vv reflect.Value for i := 0; i < l1v.Len(); i++ { - elem := l1v.Index(i) - if !ns.In(r.Interface(), elem.Interface()) { - r = reflect.Append(r, elem) + l1vv = l1v.Index(i) + if !ns.In(r.Interface(), l1vv.Interface()) { + r = reflect.Append(r, l1vv) } } for j := 0; j < l2v.Len(); j++ { - elem := l2v.Index(j) - if !ns.In(r.Interface(), elem.Interface()) { - r = reflect.Append(r, elem) + l2vv := l2v.Index(j) + + switch l1vv.Kind() { + case reflect.String: + l2t, err := toString(l2vv) + if err == nil && !ns.In(r.Interface(), l2t) { + r = reflect.Append(r, reflect.ValueOf(l2t)) + } + case reflect.Int: + l2t, err := toInt(l2vv) + if err == nil && !ns.In(r.Interface(), l2t) { + r = reflect.Append(r, reflect.ValueOf(int(l2t))) + } + case reflect.Int8: + l2t, err := toInt(l2vv) + if err == nil && !ns.In(r.Interface(), l2t) { + r = reflect.Append(r, reflect.ValueOf(int8(l2t))) + } + case reflect.Int16: + l2t, err := toInt(l2vv) + if err == nil && !ns.In(r.Interface(), l2t) { + r = reflect.Append(r, reflect.ValueOf(int16(l2t))) + } + case reflect.Int32: + l2t, err := toInt(l2vv) + if err == nil && !ns.In(r.Interface(), l2t) { + r = reflect.Append(r, reflect.ValueOf(int32(l2t))) + } + case reflect.Int64: + l2t, err := toInt(l2vv) + if err == nil && !ns.In(r.Interface(), l2t) { + r = reflect.Append(r, reflect.ValueOf(l2t)) + } + case reflect.Float32: + l2t, err := toFloat(l2vv) + if err == nil && !ns.In(r.Interface(), float32(l2t)) { + r = reflect.Append(r, reflect.ValueOf(float32(l2t))) + } + case reflect.Float64: + l2t, err := toFloat(l2vv) + if err == nil && !ns.In(r.Interface(), l2t) { + r = reflect.Append(r, reflect.ValueOf(l2t)) + } + case reflect.Interface: + switch l1vv.Interface().(type) { + case string: + switch l2vvActual := l2vv.Interface().(type) { + case string: + if !ns.In(r.Interface(), l2vvActual) { + r = reflect.Append(r, l2vv) + } + } + case int, int8, int16, int32, int64: + switch l2vvActual := l2vv.Interface().(type) { + case int, int8, int16, int32, int64: + if !ns.In(r.Interface(), l2vvActual) { + r = reflect.Append(r, l2vv) + } + } + case uint, uint8, uint16, uint32, uint64: + switch l2vvActual := l2vv.Interface().(type) { + case uint, uint8, uint16, uint32, uint64: + if !ns.In(r.Interface(), l2vvActual) { + r = reflect.Append(r, l2vv) + } + } + case float32, float64: + switch l2vvActual := l2vv.Interface().(type) { + case float32, float64: + if !ns.In(r.Interface(), l2vvActual) { + r = reflect.Append(r, l2vv) + } + } + } } } diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go index 9943f439e..811cb4c87 100644 --- a/tpl/collections/collections_test.go +++ b/tpl/collections/collections_test.go @@ -571,21 +571,45 @@ func TestUnion(t *testing.T) { expect interface{} isErr bool }{ + {nil, nil, []interface{}{}, false}, + {nil, []string{"a", "b"}, []string{"a", "b"}, false}, + {[]string{"a", "b"}, nil, []string{"a", "b"}, false}, + + // []A ∪ []B + {[]string{"1", "2"}, []int{3}, []string{}, false}, + {[]int{1, 2}, []string{"1", "2"}, []int{}, false}, + + // []T ∪ []T {[]string{"a", "b", "c", "c"}, []string{"a", "b", "b"}, []string{"a", "b", "c"}, false}, {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b", "c"}, false}, {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{"a", "b", "c", "d", "e"}, false}, {[]string{}, []string{}, []string{}, false}, - {[]string{"a", "b"}, nil, []string{"a", "b"}, false}, - {nil, []string{"a", "b"}, []string{"a", "b"}, false}, - {nil, nil, make([]interface{}, 0), true}, - {[]string{"1", "2"}, []int{1, 2}, make([]string, 0), false}, - {[]int{1, 2}, []string{"1", "2"}, make([]int, 0), false}, {[]int{1, 2, 3}, []int{3, 4, 5}, []int{1, 2, 3, 4, 5}, false}, {[]int{1, 2, 3}, []int{1, 2, 3}, []int{1, 2, 3}, false}, {[]int{1, 2, 4}, []int{2, 4}, []int{1, 2, 4}, false}, {[]int{2, 4}, []int{1, 2, 4}, []int{2, 4, 1}, false}, {[]int{1, 2, 4}, []int{3, 6}, []int{1, 2, 4, 3, 6}, false}, {[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4, 1.1}, false}, + {[]interface{}{"a", "b", "c", "c"}, []interface{}{"a", "b", "b"}, []interface{}{"a", "b", "c"}, false}, + + // []T ∪ []interface{} + {[]string{"1", "2"}, []interface{}{"9"}, []string{"1", "2", "9"}, false}, + {[]int{2, 4}, []interface{}{1, 2, 4}, []int{2, 4, 1}, false}, + {[]int8{2, 4}, []interface{}{int8(1), int8(2), int8(4)}, []int8{2, 4, 1}, false}, + {[]int8{2, 4}, []interface{}{1, 2, 4}, []int8{2, 4, 1}, false}, + {[]int16{2, 4}, []interface{}{1, 2, 4}, []int16{2, 4, 1}, false}, + {[]int32{2, 4}, []interface{}{1, 2, 4}, []int32{2, 4, 1}, false}, + {[]int64{2, 4}, []interface{}{1, 2, 4}, []int64{2, 4, 1}, false}, + {[]float64{2.2, 4.4}, []interface{}{1.1, 2.2, 4.4}, []float64{2.2, 4.4, 1.1}, false}, + {[]float32{2.2, 4.4}, []interface{}{1.1, 2.2, 4.4}, []float32{2.2, 4.4, 1.1}, false}, + + // []interface{} ∪ []T + {[]interface{}{"a", "b", "c", "c"}, []string{"a", "b", "b"}, []interface{}{"a", "b", "c"}, false}, + {[]interface{}{}, []string{}, []interface{}{}, false}, + {[]interface{}{1, 2}, []int{2, 3}, []interface{}{1, 2, 3}, false}, + {[]interface{}{1, 2}, []int8{2, 3}, []interface{}{1, 2, int8(3)}, false}, + {[]interface{}{1.1, 2.2}, []float64{2.2, 3.3}, []interface{}{1.1, 2.2, 3.3}, false}, + // errors {"not array or slice", []string{"a"}, false, true}, {[]string{"a"}, "not array or slice", false, true},