seo*_*ady 1 javascript struct dynamic go slice
我正在学习围棋,到目前为止真的很享受。来自 JS 背景,我仍在发现某些模式和最佳实践。
使用对象路径在 Go 中为深度嵌套的对象获取和分配值的最佳方法是什么?例如,在 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)Run Code Online (Sandbox Code Playgroud)
如果你需要一个通用的解决方案,你可以使用 package 来完成reflect,但如果可能的话最好避免它(例如,如果你在编译时知道类型和“路径”,只需使用字段选择器和索引表达式)。
这是一个演示。设置由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))
}
Run Code Online (Sandbox Code Playgroud)
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)
}
Run Code Online (Sandbox Code Playgroud)
这是我们可以测试它的方法:
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)
}
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
[{Children:[{Children:[{Children:[] A:1}] A:0}] A:0}]
[{Children:[{Children:[{Children:[] A:2}] A:0}] A:0}]
Run Code Online (Sandbox Code Playgroud)
从输出中可以看出,路径A表示的“deep”字段从初始变为.string"0.Children.0.Children.0.A"12
需要注意的是结构的字段(Foo.A和Foo.Children在这种情况下)必须出口(必须以大写字母开头),否则其他的包将无法访问这些领域,其价值无法用包被改变reflect。
无需反射,事先知道类型和“路径”,就可以这样完成(继续前面的例子):
f := &x[0].Children[0].Children[0]
fmt.Printf("%+v\n", f)
f.A = 3
fmt.Printf("%+v\n", f)
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
&{Children:[] A:2}
&{Children:[] A:3}
Run Code Online (Sandbox Code Playgroud)
这个的一般解决方案(没有反射):
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
}
Run Code Online (Sandbox Code Playgroud)
使用它(再次,继续前面的例子):
path = "0.0.0"
f2 := getFoo(x, strings.Split(path, ".")...)
fmt.Printf("%+v\n", f2)
f2.A = 4
fmt.Printf("%+v\n", f2)
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
&{Children:[] A:3}
&{Children:[] A:4}
Run Code Online (Sandbox Code Playgroud)
但是请注意,如果我们只处理int索引,那么声明path为...string(即[]string)就没有意义了,int切片会更有意义。