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.
Define the interface.
type Injectable interface {
InjectTo(v interface{})
}
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
}
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)
}
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.