如何将嵌套结构成型为没有嵌套循环的另一个结构?

How can I inject A and B's selected values into children C below?

decoder.go (Playground link)

package main

import (
    "fmt"
)

type Input struct {
    A []A
}

type A struct {
    AID int
    B []B
}

type B struct {
    BID int
    C []C
}

type C struct {
    // I want to inject only AID and BID here
    // But, without injecting A and B directly
    //   (without recursively)
    CID int
}

func main() {
    res := Input{
        A: []A {
            A {
                AID: 1,
                B: []B {
                    B{ BID: 11, C: []C{{ 111 }, { 111 }}},
                    B{ BID: 12, C: []C{{ 121 }, { 122 }}},
                },
            },
            A {
                AID: 2,
                B: []B {
                    B{ BID: 21, C: []C{{ 211 }, { 211 }}},
                    B{ BID: 22, C: []C{{ 221 }, { 222 }}},
                },
            },
        },
    }

    // I want to inject AID and BID into C
    // WITHOUT nested loops like this:
    for _, a := range res.A {
        for _, b := range a.B {
            for _, c := range b.C {
                fmt.Println(a.AID, b.BID, c.CID)
            }
        }
    }
}

If you don't want to use nested loop, one solution is using recursive call and reflection to inject the attributes/properties into a struct. In the following implementation, attributes/properties to be injected is wrapped in a struct implement Injectable interface. Working example can be found at Go Playground.

  1. Define the interface.

    type Injectable interface {
        InjectTo(v interface{})
    }
    
  2. Define data structure that holds properties/attributes to be injected, e.g.

    type Property struct {
        AID int
        BID int
    }
    
    type C struct {
        // The properties will be injected here
        Property
        CID int
    }
    
  3. Implement InjectTo using reflection and recursive call.

    //Method must be pointer receiver since p will be used 
    //as temporary placeholder for parent properties/attributes.
    func (p *Property) injectRecursive(v reflect.Value, it reflect.Type, pv reflect.Value) {
        switch v.Kind() {
        case reflect.Struct:
            vt := v.Type()
            //Embedded struct is a 'value' type implement Injectable
            if vt.Implements(it) {
                //Inject value to embedded struct
                ot := pv.Type()
                for k := 0; k < pv.NumField(); k++ {
                    name := ot.Field(k).Name
                    f := v.FieldByName(name)
                    if f.CanSet() {
                        f.Set(pv.Field(k))
                    }
                }
            } else {
                for k := 0; k < v.NumField(); k++ {
                    fv := v.Field(k)
    
                    //Match by field name.
                    //For more robust and generic solution
                    //consider using other approach, e.g. tag
                    f := pv.FieldByName(vt.Field(k).Name)
                    if f.CanSet() {
                        f.Set(fv)
                    } else {
                        p.injectRecursive(fv, it, pv)
                    }
                }
            }
        case reflect.Slice, reflect.Array:
            for k := 0; k < v.Len(); k++ {
                p.injectRecursive(v.Index(k), it, pv)
            }
        case reflect.Ptr:
            if v.IsValid() {
                p.injectRecursive(v.Elem(), it, pv)
            }
        }
    }
    
    //InjectTo must be Value (not pointer) receiver
    func (p Property) InjectTo(s interface{}) {
        sv := reflect.Indirect(reflect.ValueOf(s))
        pv := reflect.Indirect(reflect.ValueOf(&p))
        it := reflect.TypeOf((*Injectable)(nil)).Elem()
        p.injectRecursive(sv, it, pv)
    }
    
  4. You can inject the properties by:

    res := Input{...}
    prop := Property{}
    prop.InjectTo(&res)
    

Go generally encourages an explicit approach, and nested for loops are very explicit in terms of what is actually going on here. There's likely no need to try to find a more concise solution and Go likely won't offer it.