2017-06-08 72 views
-2

如何将A和B的选定值注入下面的孩子C?如何在没有嵌套循环的情况下将嵌套结构体塑造成另一个结构体?

decoder.goPlayground 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) 
      } 
     } 
    } 
} 
+0

@EricPauley我明白了,THX。我想知道我是否做得正确。 –

+1

持续点击关闭链接的人不遵循StackOverflow指南。 _请编辑问题,将其限制为具有足够详细信息的特定问题以确定适当的答案。避免一次询问多个不同的问题。这是一个具有足够细节的特定问题,那么为什么你点击关闭? –

+0

请编辑具有足够细节的问题以确定合适的答案。目前还不清楚你想要做什么。例如,你的意思是:“我想将AID和BID注入C”,其中'C struct {CID int} C没有'AID'或'BID'。你注意什么? – peterSO

回答

1

如果您不想使用嵌套循环,则一种解决方案是使用递归调用和反射将属性/属性注入到结构中。在下面的实现中,要注入的属性/属性被包装在一个结构实现接口中。工作示例可在Go Playground找到。

  1. 定义接口。

    type Injectable interface { 
        InjectTo(v interface{}) 
    } 
    
  2. 定义包含要注入的属性/属性的数据结构,例如,

    type Property struct { 
        AID int 
        BID int 
    } 
    
    type C struct { 
        // The properties will be injected here 
        Property 
        CID int 
    } 
    
  3. 使用反射和递归调用实现InjectTo

    //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. 您可以通过注入性质:

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

哇,thx,这很有趣! –

0

转到一般鼓励一个明确的办法,并嵌套for循环中的什么是真正回事方面是非常明确的。可能不需要尝试找到更简洁的解决方案,Go很可能不会提供它。