2016-12-27 2 views
0

私はGoを学習していて、これまで本当に楽しんでいます。 JSの背景から来て、私はまだ発見している特定のパターンとベストプラクティスがあります。Golangは深い入れ子構造に値を割り当てます

オブジェクトパスを使用して、Goの深くネストされたオブジェクトに値を取得して割り当てるにはどうすればよいでしょうか?例えば、JSであなたが一般的な解決策が必要な場合は、パッケージreflectを使用してそれを行うことができます

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 [0] [" x "] ...'を実行するだけです。他の慣用的なショートカットはありません。 – Nadh

+0

Pythonのリスト内包は、このような機能操作を行うための別の方法を提供します。 [Goでのリスト内包に関する別の質問に対処するこの回答(http://stackoverflow.com/a/27848524/539810)は、それが可能だが価値がないことを説明しているので、読む価値がある。 –

答えて

2

...このように行うことができるが、それが可能であればそれを避ける方が良いでしょう(コンパイル時にタイプとパスが分かっている場合は、フィールド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"2初期1から変更することによって示される「深い」フィールド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

関連する問題