Created
March 21, 2023 01:48
-
-
Save abemedia/a0fa727e7d7ce4b06dfc7deba06296eb to your computer and use it in GitHub Desktop.
Compare equal structs with different field order
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main_test | |
import ( | |
"reflect" | |
"testing" | |
"github.com/google/go-cmp/cmp" | |
) | |
func TestEqual(t *testing.T) { | |
a := struct { | |
Foo string | |
Bar string | |
}{"foo", "bar"} | |
b := struct { | |
Bar string | |
Foo string | |
}{"bar", "foo"} | |
opt := cmp.FilterPath( | |
func(p cmp.Path) bool { return true }, | |
cmp.Comparer(compare), | |
) | |
if diff := cmp.Diff(a, b, opt); diff != "" { | |
t.Errorf(diff) | |
} | |
} | |
func compare(a, b any) bool { | |
if a == nil || b == nil { | |
return a == b | |
} | |
if !compareTypes(reflect.TypeOf(a), reflect.TypeOf(b)) { | |
return false | |
} | |
return compareValues(reflect.ValueOf(a), reflect.ValueOf(b)) | |
} | |
func compareTypes(a, b reflect.Type) bool { | |
if a == nil || b == nil { | |
return a == b | |
} | |
if a.Kind() != b.Kind() { | |
return false | |
} | |
switch a.Kind() { | |
case reflect.Pointer, reflect.Slice: | |
return compareTypes(a.Elem(), b.Elem()) | |
case reflect.Struct: | |
if a.NumField() != b.NumField() { | |
return false | |
} | |
af := map[string]reflect.StructField{} | |
bf := map[string]reflect.StructField{} | |
for i := 0; i < a.NumField(); i++ { | |
af[a.Field(i).Name] = a.Field(i) | |
bf[b.Field(i).Name] = b.Field(i) | |
} | |
for k := range af { | |
if _, ok := bf[k]; !ok { | |
return false | |
} | |
if af[k].Tag != bf[k].Tag { | |
return false | |
} | |
if !compareTypes(af[k].Type, bf[k].Type) { | |
return false | |
} | |
} | |
return true | |
default: | |
return a.Kind() == b.Kind() | |
} | |
} | |
func compareValues(a, b reflect.Value) bool { | |
switch a.Kind() { | |
case reflect.Pointer: | |
return compareValues(a.Elem(), b.Elem()) | |
case reflect.Slice: | |
if a.Len() != b.Len() { | |
return false | |
} | |
for i := 0; i < a.Len(); i++ { | |
if !compareValues(a.Index(i), b.Index(i)) { | |
return false | |
} | |
} | |
return true | |
case reflect.Struct: | |
av := map[string]reflect.Value{} | |
bv := map[string]reflect.Value{} | |
for i := 0; i < a.Type().NumField(); i++ { | |
av[a.Type().Field(i).Name] = a.Field(i) | |
bv[b.Type().Field(i).Name] = b.Field(i) | |
} | |
for k := range av { | |
if !compareValues(av[k], bv[k]) { | |
return false | |
} | |
} | |
return true | |
default: | |
return cmp.Equal(a.Interface(), b.Interface()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment