tpl/collections: Properly handle pointer types in complement/symdiff
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 7 Nov 2018 07:59:21 +0000 (08:59 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 7 Nov 2018 08:29:13 +0000 (09:29 +0100)
We cannot compare them by values, because that gets `hash of unhashable type` for the prime use case.

tpl/collections/complement_test.go
tpl/collections/reflect_helpers.go
tpl/collections/symdiff_test.go

index 4cae7556fee16491272589c5c0203a59442babd1..e5edb23d34bfe3711b590f0d76cb177e5a2f25bb 100644 (file)
@@ -23,6 +23,13 @@ import (
        "github.com/stretchr/testify/require"
 )
 
+type StructWithSlice struct {
+       A string
+       B []string
+}
+
+type StructWithSlicePointers []*StructWithSlice
+
 func TestComplement(t *testing.T) {
        t.Parallel()
 
@@ -33,10 +40,13 @@ func TestComplement(t *testing.T) {
        s1 := []TstX{TstX{A: "a"}, TstX{A: "b"}, TstX{A: "d"}, TstX{A: "e"}}
        s2 := []TstX{TstX{A: "b"}, TstX{A: "e"}}
 
-       xa, xd := &TstX{A: "a"}, &TstX{A: "d"}
+       xa, xb, xd, xe := &StructWithSlice{A: "a"}, &StructWithSlice{A: "b"}, &StructWithSlice{A: "d"}, &StructWithSlice{A: "e"}
+
+       sp1 := []*StructWithSlice{xa, xb, xd, xe}
+       sp2 := []*StructWithSlice{xb, xe}
 
-       sp1 := []*TstX{xa, &TstX{A: "b"}, xd, &TstX{A: "e"}}
-       sp2 := []*TstX{&TstX{A: "b"}, &TstX{A: "e"}}
+       sp1_2 := StructWithSlicePointers{xa, xb, xd, xe}
+       sp2_2 := StructWithSlicePointers{xb, xe}
 
        for i, test := range []struct {
                s        interface{}
@@ -49,7 +59,8 @@ func TestComplement(t *testing.T) {
                {[]int{1, 2, 3, 4, 5}, []interface{}{[]int{1, 3}, []string{"a", "b"}, []int{1, 2}}, []int{4, 5}},
                {[]int{1, 2, 3, 4, 5}, []interface{}{[]int64{1, 3}}, []int{2, 4, 5}},
                {s1, []interface{}{s2}, []TstX{TstX{A: "a"}, TstX{A: "d"}}},
-               {sp1, []interface{}{sp2}, []*TstX{xa, xd}},
+               {sp1, []interface{}{sp2}, []*StructWithSlice{xa, xd}},
+               {sp1_2, []interface{}{sp2_2}, StructWithSlicePointers{xa, xd}},
 
                // Errors
                {[]string{"a", "b", "c"}, []interface{}{"error"}, false},
index 85aa389ce403e75ef8bc334656c23c6792bb7c08..fca65481f30062bb36e1cbb8915a5d6794f56ac6 100644 (file)
@@ -43,7 +43,6 @@ func numberToFloat(v reflect.Value) (float64, error) {
 }
 
 // normalizes different numeric types to make them comparable.
-// If not, any pointer will be unwrapped.
 func normalize(v reflect.Value) interface{} {
        k := v.Kind()
 
@@ -53,8 +52,6 @@ func normalize(v reflect.Value) interface{} {
                if err == nil {
                        return f
                }
-       case k == reflect.Ptr:
-               v = v.Elem()
        }
 
        return v.Interface()
@@ -70,6 +67,7 @@ func collectIdentities(seqs ...interface{}) (map[interface{}]bool, error) {
                case reflect.Array, reflect.Slice:
                        for i := 0; i < v.Len(); i++ {
                                ev, _ := indirectInterface(v.Index(i))
+
                                if !ev.Type().Comparable() {
                                        return nil, errors.New("elements must be comparable")
                                }
index d44999730c0f71ba26651710d61cf4d41a9785b4..4024ffe7a7bfd9bab592bb89f4f278d53671a10d 100644 (file)
@@ -33,10 +33,10 @@ func TestSymDiff(t *testing.T) {
        s1 := []TstX{TstX{A: "a"}, TstX{A: "b"}}
        s2 := []TstX{TstX{A: "a"}, TstX{A: "e"}}
 
-       xa, xd := &TstX{A: "a"}, &TstX{A: "d"}
+       xa, xb, xd, xe := &StructWithSlice{A: "a"}, &StructWithSlice{A: "b"}, &StructWithSlice{A: "d"}, &StructWithSlice{A: "e"}
 
-       sp1 := []*TstX{xa, &TstX{A: "b"}, xd, &TstX{A: "e"}}
-       sp2 := []*TstX{&TstX{A: "b"}, &TstX{A: "e"}}
+       sp1 := []*StructWithSlice{xa, xb, xd, xe}
+       sp2 := []*StructWithSlice{xb, xe}
 
        for i, test := range []struct {
                s1       interface{}
@@ -49,7 +49,7 @@ func TestSymDiff(t *testing.T) {
                {[]int{1, 2, 3}, []int{3, 4}, []int{1, 2, 4}},
                {[]int{1, 2, 3}, []int64{3, 4}, []int{1, 2, 4}},
                {s1, s2, []TstX{TstX{A: "b"}, TstX{A: "e"}}},
-               {sp1, sp2, []*TstX{xa, xd}},
+               {sp1, sp2, []*StructWithSlice{xa, xd}},
 
                // Errors
                {"error", "error", false},