dongzhan8001 2017-06-08 17:41 采纳率: 0%
浏览 27
已采纳

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

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)
            }
        }
    }
}
  • 写回答

2条回答 默认 最新

  • ds1379551 2017-06-09 01:16
    关注

    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)
      
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 蓝牙耳机怎么查看日志
  • ¥15 Fluent齿轮搅油
  • ¥15 八爪鱼爬数据为什么自己停了
  • ¥15 交替优化波束形成和ris反射角使保密速率最大化
  • ¥15 树莓派与pix飞控通信
  • ¥15 自动转发微信群信息到另外一个微信群
  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏