dpdhf02040 2016-12-27 02:36
浏览 64
已采纳

Golang将值分配给深层嵌套结构

I'm learning Go and so far really enjoying it. Coming from a JS background, there are certain patterns and best practices that I'm still discovering.

What would be the best way to get and assign a value to a deeply nested object in Go using a Object path? For example, in JS it could be done like this...

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)

</div>
  • 写回答

1条回答 默认 最新

  • doulangbizhan5160 2016-12-27 16:47
    关注

    If you need a general solution, you can do it using package reflect, but it's better to avoid it if possible (e.g. if you know the types and the "path" at compile time, simply use field selectors and index expressions).

    Here's a demonstration. A helper function that sets a "deep" value specified by string elements may look like this:

    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))
    }
    

    The index() function used above may look like this:

    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)
    }
    

    This is how we can test it:

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

    Output (try it on the Go Playground):

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

    As can be seen from the output, the "deep" field A denoted by the string path "0.Children.0.Children.0.A" changed from the initial 1 to 2.

    Note that fields of structs (Foo.A and Foo.Children in this case) must be exported (must start with capital letter), else other packages would not be able to access those fields, and their value could not be changed using package reflect.


    Without reflection, knowing the types and "path" prior, it can be done like this (continuing the previous example):

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

    Output (try it on the Go Playground):

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

    The general solution of this (without reflection):

    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
    }
    

    Using it (again, continuing the previous example):

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

    Output (try it on the Go Playground):

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

    But note that if we're only dealing with int indices, it does not make sense anymore to declare path to be ...string (which is []string), an int slice would make a lot more sense.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分