2016-12-27 91 views
0

我正在学Go,到目前为止真的很享受它。从JS背景来看,我仍然发现了某些模式和最佳实践。Golang将值分配给深层嵌套结构

在Go中使用Object路径获取和赋值给深层嵌套对象的最佳方法是什么?例如,在JS这是可以做到这样的...

var children = [{children:[{children:[{a:1}]}]}] 
 
var child = "0.children.0.children.0".split('.').reduce((c, p) => c[p], children) 
 
child.a = 2 
 
console.log(children[0].children[0].children[0].a)

+3

嵌套结构,你只需要做'A.B.C ...'。使用地图,数组或片,您只需执行[a] [[x]] ...。没有其他惯用的捷径。 – Nadh

+0

Python的列表解析提供了一种不同的方式来完成这样的功能操作。 [这个回答解决了另一个涉及Go的列表解析的问题](http://stackoverflow.com/a/27848524/539810)值得一读,因为它解释了它可能但不值得。 –

回答

2

如果你需要一个通用的解决方案,你可以使用包reflect做到这一点,但最好避免它如果可能的话(例如,如果您知道编译时的类型和“路径”,只需使用字段selectorsindex expressions)。

这是一个演示。该设置由string元素指定一个“深”值一个辅助函数可能看起来像这样:

func set(d interface{}, value interface{}, path ...string) { 
    v := reflect.ValueOf(d) 
    for _, s := range path { 
     v = index(v, s) 
    } 
    v.Set(reflect.ValueOf(value)) 
} 

上面使用可能看起来像这样的index()功能:

func index(v reflect.Value, idx string) reflect.Value { 
    if i, err := strconv.Atoi(idx); err == nil { 
     return v.Index(i) 
    } 
    return v.FieldByName(idx) 
} 

这是我们如何能够测试:

type Foo struct { 
    Children []Foo 
    A  int 
} 

func main() { 
    x := []Foo{ 
     { 
      Children: []Foo{ 
       { 
        Children: []Foo{ 
         { 
          A: 1, 
         }, 
        }, 
       }, 
      }, 
     }, 
    } 
    fmt.Printf("%+v\n", x) 
    path := "0.Children.0.Children.0.A" 
    set(x, 2, strings.Split(path, ".")...) 
    fmt.Printf("%+v\n", x) 
} 

输出(尝试在Go Playground):

[{Children:[{Children:[{Children:[] A:1}] A:0}] A:0}] 
[{Children:[{Children:[{Children:[] A:2}] A:0}] A:0}] 

如可以从输出,由string路径"0.Children.0.Children.0.A"从初始1变为2表示的“深”字段A看到。

注意结构(在这种情况下Foo.AFoo.Children)的字段必须出口(必须以大写字母),否则其他的包将无法访问这些领域,其价值无法用包reflect改变。


没有反射,已知类型和 “路径” 之前,就可以这样来进行(继续前面的例子):

f := &x[0].Children[0].Children[0] 
fmt.Printf("%+v\n", f) 
f.A = 3 
fmt.Printf("%+v\n", f) 

输出(尝试在Go Playground):

&{Children:[] A:2} 
&{Children:[] A:3} 

这样做的一般溶液(无反射):

func getFoo(x []Foo, path ...string) (f *Foo) { 
    for _, s := range path { 
     if i, err := strconv.Atoi(s); err != nil { 
      panic(err) 
     } else { 
      f = &x[i] 
      x = f.Children 
     } 
    } 
    return 
} 

使用它(再次,继续前面的例子):

path = "0.0.0" 
f2 := getFoo(x, strings.Split(path, ".")...) 
fmt.Printf("%+v\n", f2) 
f2.A = 4 
fmt.Printf("%+v\n", f2) 

输出(尝试在Go Playground):

&{Children:[] A:3} 
&{Children:[] A:4} 

但要注意的是,如果我们只处理int指数,它宣称path...string(这是[]string),int切片会更有意义。

+0

谢谢你。很好的例子! – seogrady

+0

我会怎么做,没有反思。例如,使用只有整数“0.0.0”的路径,然后返回可以被突变的结构Foo? – seogrady

+0

@seogrady请参阅已编辑的答案。 – icza