2016-12-27 45 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