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.
            */
        }

    }
}

>Solution :

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)
}

Leave a Reply