如何将一个嵌套的结构转换为另一个结构,而不使用嵌套循环?

huangapple go评论130阅读模式
英文:

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找到工作示例。

  1. 定义接口。
type Injectable interface {
    InjectTo(v interface{})
}
  1. 定义包含要注入的属性/属性的数据结构,例如:
type Property struct {
    AID int
    BID int
}

type C struct {
    // 属性将被注入到这里
    Property
    CID int
}
  1. 使用反射和递归调用实现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)
}
  1. 您可以通过以下方式注入属性:
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.

  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 &#39;value&#39; type implement Injectable
    if vt.Implements(it) {
    //Inject value to embedded struct
    ot := pv.Type()
    for k := 0; k &lt; 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 &lt; 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 &lt; 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(&amp;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(&amp;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.

huangapple
  • 本文由 发表于 2017年6月9日 01:41:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/44442336.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定