How can I compare two structs of not the same type? - go

How can I check if two structs that are not of the same type are equal?
Meaning if we have struct of typeA and struct of typeB, if in both structs we have same amount of fields with the same types - they are equal.
type layoutA struct {
A int
}
type layoutB layoutA
reflect.TypeOf(layoutA{}) == reflect.TypeOf(layoutB{}) // false
cmp.Equal(layoutA{}, layoutB{}) // false
compareStructs(layoutA{}, layoutB{}) // true - need to find this function
cmp package -> "github.com/google/go-cmp/cmp"

If the order of the fields needs to match as well you can just check for "convertability" using the ConvertibleTo method of the reflect.Type type.
If the order doesn't matter then you'll have to compare each individual field, a bit more work, but still very basic. First check that both types have the same number of fields, then loop over the fields of one type and check that every field that's present in that type is also present in the other type by using the FieldByName method and then compare the types of those fields.

You can compare the field types by using reflection with something like this:
func compareStructs(s1 interface{}, s2 interface{}) bool {
type1 := reflect.TypeOf(s1)
type2 := reflect.TypeOf(s2)
numField1 := type1.NumField()
numField2 := type2.NumField()
if numField1 != numField2 {
return false
}
for i := 0; i < numField1; i++ {
field1 := type1.Field(i)
field2 := type2.Field(i)
if field1.Type != field2.Type {
return false
}
}
return true
}

Related

How to assign to struct fields from an array of values in order?

I know you can create a struct with a literal, listing the fields in order:
type Foo struct {
A string
B string
C string
}
foo := Foo{ "foo", "bar", "baz" }
Is there any way to do the same thing dynamically? I have an array of values (actually an array of arrays) and I want to assign them to an array of structs in field order, and there are rather more than three fields. Is there a way to say "assign this struct's fields from this array of values in order"? I really don't want to write a bunch of structArray[i].field1 = dataArray[i][0]; structArray[i].field2 = dataArray[i][1], etc.
My thoughts so far have been to use reflect, which seems overkillish, or maybe to create an array of field names and build json strings and unmarshal them. Any better ideas?
With reflection you can write a function like this:
func populate(dst any, src any) {
v := reflect.ValueOf(dst)
if v.Type().Kind() != reflect.Pointer {
panic("dst must be a pointer")
}
v = v.Elem()
if v.Type().Kind() != reflect.Struct {
panic("dst must be a pointer to struct")
}
w := reflect.ValueOf(src)
if w.Type().Kind() != reflect.Slice {
panic("src must be a slice")
}
for i := 0; i < v.NumField(); i++ {
// in case you need to support source slices of arbitrary types
value := w.Index(i)
if value.Type().Kind() == reflect.Interface {
value = value.Elem()
}
v.Field(i).Set(value)
}
}
You must make sure that dst is addressable, hence pass a pointer to Foo into populate; and that the i-th element in the source slice is actually assignable to the corresponding i-th field in the struct.
The code above is in a quite simplified form. You can add additional checks to it, e.g. with CanAddr or AssignableTo, if you think callers may misbehave.
Call it like:
func main() {
f := Foo{}
populate(&f, []string{"foo", "bar", "baz"})
fmt.Println(f) // {foo bar baz}
}
Here's a playground that also shows that you can pass a slice of []any as the source slice, in case the struct fields aren't all the same type: https://go.dev/play/p/G8qjDCt79C7

Iterating slice struct within struct using reflection

I'm trying to achieve the following:
Use-case:
I have three structures, I need to compare 2 of those against one. (in the example described as: a & b need to be compared against full)
Reflection is used to loop over every field, retrieve the name of the field. And comparing the difference between a & full, b & full, storing the results in a shared structure.
If the field equals World, we know it's a slice struct:
I need to retrieve the first index of the Bar slice within the Foo structure.
Even though the variable is a slice, I know it will always have a length of 1 in this use-case.
When retrieved I need to loop over those fields, like what is happening in the previous if statement.
Example code:
type Foo struct {
Hello string
World []Bar
}
type Bar struct {
Fish string
}
type Result struct {
Field string
Correct_A bool
Distance_A int
Correct_B bool
Distance_B int
Result []Result
}
func compare_structs() {
var full, a, b Foo
// filling in all variables...
result := []Result{}
rfx_f := reflect.ValueOf(full)
rfx_a := reflect.ValueOf(a)
rfx_b := reflect.ValueOf(b)
type_result := rfx_f.Type()
for i := 0; i < rfx_f.NumField(); i++ {
tmp_res := Result{
Field: type_result.Field(i).Name,
}
if reflect.TypeOf(full).Field(i).Type.Kind() != reflect.Slice {
value := rfx_f.Field(i).Interface()
value_a := rfx_a.FieldByName(tmp_res.Field).Interface()
value_b := rfx_b.FieldByName(tmp_res.Field).Interface()
// functions to compare the values of this field
tmp_res.compare(value, value_a, value_b)
tmp_res.lev(value, value_a, value_b)
result = append(result, tmp_res)
} else if tmp_res.Field == "World" {
/*
I need to retrieve the first index of the Bar slice within the Foo structure.
Even though the variable is a slice, I know it will always have a length of 1 in this use-case.
When retrieved I need to loop over those fields, like what is happening in the previous if statement.
*/
}
}
}
You first need to get the field:
wordField:=rfx_f.Field(i)
which you know to be a slice, so you index it to get the first element
item:=wordField.Index(0)
This will panic if index is out of range.
Then you can iterate the fields:
for fieldIx:=0;fieldIx<item.NumField();fieldIx++ {
field:=item.Field(fieldIx)
}

Check if struct field is empty

I would like to iterate over the fields of a struct after unmarshalling a JSON object into it and check for the fields whose value was not set (i.e. are empty).
I can get the value of each field and compare that to the reflect.Zero value for the corresponding type
json.Unmarshal([]byte(str), &res)
s := reflect.ValueOf(&res).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
v := reflect.ValueOf(f.Interface())
if (reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())) {
....
But the problem, of course, is that this will not work well for bool or int values.
If a bool field is set to false in the JSON or an int field is set to 0, they will be equal to the zero value of their type. The aforementioned check will consider the fields to be uninitialized, even though they actually have a value set.
I know one way around this is to use pointers, but I just don't see how that would be possible in this case as I'm working with reflect.Value types, not the actual struct.
As you've mentioned, you could use pointers.
The json package can handle unmarshalling values into pointers for you. You've not included the json payload you are trying to unmarshal, or the struct you are unmarshalling into, so I've made up an example.
// json
{
"foo": true,
"number_of_foos": 14
}
// go struct
type Foo struct {
Present bool `json:"foo"`
Num int `json:"number_of_foos"`
}
Here if the keys foo or number_of_foos is missing, then as you've correctly observed, the zero value (false/ 0) will be used. In general the best advice is to make use of the zero value. Create structures so that zero values of false are useful, rather than a pain.
This is not always possible, so changing the types of the fields in the Foo struct to be pointers will allow you to check the 3 cases you are after.
Present
Present and zero
Missing
here is the same struct with pointers:
// go struct
type Foo struct {
Present *bool `json:"foo"`
Num *int `json:"number_of_foos"`
}
Now you can check for presence of the value with fooStruct.Present != nil and if that condition holds, you can assume that the value in the field is the one you wanted.
There is no need to use the reflect package.
Another way of doing the same is by implementing json.Unmarshaler.
type MaybeInt struct {
Present bool
Value int
}
func (i *MaybeInt) UnmarshalJSON(bs []byte) error {
if e := json.Unmarshal(bs, &i.Value); e != nil {
return e
}
i.Present = true
return nil
}
You can then use MaybeInt in your top-level structure:
type Top struct {
N MaybeInt `json:"n"`
M MaybeInt `json:"m"`
}
func main() {
t := Top{}
if e := json.Unmarshal([]byte(` { "n": 4930 } `), &t); e != nil {
panic(e)
}
fmt.Println(t.N, t.M)
}
See it working on the playground
Try using the golang validator package. The validator package offers a required attribute that might do the required job for your need. The official documentation for required attribute states:
This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil.
The example illustrating the same can be seen at: https://github.com/go-playground/validator/blob/v9/_examples/struct-level/main.go.
Hope this solves your requirement.

How to sort struct fields in alphabetical order

How could I get an output of struct, sorted by fields?
type T struct {
B int
A int
}
t := &T{B: 2, A: 1}
doSomething(t)
fmt.Println(t) // &{1 2} --> Sorted by fields
A struct is an ordered collection of fields. The fmt package uses reflection to get the fields and values of a struct value, and generates output in the order in which they were defined.
So the simplest solution would be to declare your type where you already have your fields arranged in alphabetical order:
type T struct {
A int
B int
}
If you can't modify the order of fields (e.g. memory layout is important), you can implement the Stringer interface by specifying a String() method for your struct type:
func (t T) String() string {
return fmt.Sprintf("{%d %d}", t.A, t.B)
}
The fmt package checks if the passed value implements Stringer, and if it does, calls its String() method to generate the output.
Cons of this solution is that this is not flexible (e.g. if you add a new field, you have to update the String() method too), also you have to do it for every struct type you want it to work (and you can't define methods for types defined in other packages).
The completely flexible solution can use reflection. You can get the names of fields, sort them by name, and then iterate over the sorted names and get the field values (by name).
Pros of this solution is that this works for any struct, and it keeps working without modification even if you add or remove fields from your structs. It also works for fields of any type, not just for int fields.
Here is an example how to do it (try it on the Go Playground):
func printFields(st interface{}) string {
t := reflect.TypeOf(st)
names := make([]string, t.NumField())
for i := range names {
names[i] = t.Field(i).Name
}
sort.Strings(names)
v := reflect.ValueOf(st)
buf := &bytes.Buffer{}
buf.WriteString("{")
for i, name := range names {
val := v.FieldByName(name)
if !val.CanInterface() {
continue
}
if i > 0 {
buf.WriteString(" ")
}
fmt.Fprintf(buf, "%v", val.Interface())
}
buf.WriteString("}")
return buf.String()
}
Make T implement the Stringer interface (see package fmt) and do either print A orb B first.
BTW. This is a stupid idea.

How do I set a dynamic struct field in Go?

I'm unmarshalling JSON in Go to a map[string]interface{} and using the interface's mixed string, float and slice values to populate field values of a PlaceNode struct.
I need something like "Default values" because not all JSON objects have all Struct fields. Coming from other language backgrounds, were Structs indexable I'd be used to doing something like this to set a value on the n Placenode variable (e.g. as if it were a self or this keyword in JavaScript).
n[key] = value;
Instead, I have a method on my PlaceNode struct to read the interface{}, use reflect and optionally assign a value if the field can be set. My interfaces{} don't implement all values and so I'm having trouble unmarshmaling directly into my struct.
Apparently none of the fields pass this s.CanSet() check. So I must be going about this wrong.
How do I set a dynamic struct field in Go?
func (n PlaceNode) New(data map[string]interface{}) PlaceNode {
for key, val := range data {
n.Id = key
for k, v := range val.(map[string]interface{}) {
f := reflect.ValueOf(v)
st := reflect.ValueOf(n)
if (st.Kind() == reflect.Struct) {
s := st.FieldByName(k)
if f.Kind() == reflect.String && true == s.CanSet() {
s.SetString(f.String());
} else if f.Kind() == reflect.Float64 && true == s.CanSet() {
s.SetFloat(f.Float());
} else if f.Kind() == reflect.Slice && true == s.CanSet() {
s.Set(f.Slice(0, f.Len()));
}
}
}
}
return n
}
The data argument map[string]interface{} has an interface that is also a map[string]interface{} and looks like this:
{
"XV.12A": {
"Area": 1189.132667,
"CensusBlock": 2032,
"CensusBlockGroup": 2,
"CensusCbsaFips": 40900,
"CensusCountyFips": 61,
"CensusMcdFips": 90160,
"CensusMsaFips": 6922,
"CensusPlaceFips": 3204,
"CensusStateFips": 6,
"CensusTract": 203,
"CensusYear": 2010,
"Radius": 19.455402434548,
"RegionSize": 1189.132667
}
}
When you make the following call:
st := reflect.ValueOf(n)
ValueOf is passed a copy of the PlaceNode struct. So any changes made to st would not be seen in n. For this reason, the package treats cases like this as non-addressable values. If you want a reflect.Value that represernts n, try using something like this:
st := reflect.ValueOf(&n).Elem()
Now st is working directly with n rather than a copy, and is addressable. You should now be able to use the various Set* methods on it and its fields.

Resources