英文:
How to shape a nested struct into another without nested loops?
问题
如何将A和B的选定值注入到下面的子项C中?
decoder.go (Playground链接)
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 {
// 我只想在这里注入AID和BID
// 但是,不要直接注入A和B
// (不要递归注入)
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 }}},
},
},
},
}
// 我想将AID和BID注入到C中
// 不使用嵌套循环,像这样:
for _, a := range res.A {
for _, b := range a.B {
for _, c := range b.C {
fmt.Println(a.AID, b.BID, c.CID)
}
}
}
}
英文:
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)
}
}
}
}
答案1
得分: 1
如果您不想使用嵌套循环,一种解决方案是使用递归调用和反射将属性/属性注入到结构体中。在以下实现中,要注入的属性/属性被包装在实现了Injectable
接口的结构体中。可以在Go Playground找到工作示例。
- 定义接口。
type Injectable interface {
InjectTo(v interface{})
}
- 定义包含要注入的属性/属性的数据结构,例如:
type Property struct {
AID int
BID int
}
type C struct {
// 属性将被注入到这里
Property
CID int
}
- 使用反射和递归调用实现
InjectTo
。
// 方法必须是指针接收器,因为p将用作父属性/属性的临时占位符。
func (p *Property) injectRecursive(v reflect.Value, it reflect.Type, pv reflect.Value) {
switch v.Kind() {
case reflect.Struct:
vt := v.Type()
// 嵌入的结构体是实现Injectable的'值'类型
if vt.Implements(it) {
// 将值注入到嵌入的结构体中
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)
// 根据字段名匹配。
// 对于更健壮和通用的解决方案,
// 考虑使用其他方法,例如标签
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必须是值(而不是指针)接收器
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)
}
- 您可以通过以下方式注入属性:
res := Input{...}
prop := Property{}
prop.InjectTo(&res)
英文:
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)
答案2
得分: 0
Go通常鼓励采用显式的方法,嵌套的for循环在实际操作中非常明确。可能没有必要试图找到更简洁的解决方案,Go语言可能也不会提供这样的解决方案。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论