douxin8749
douxin8749
2014-12-22 20:36

Golang,仅附加最后一个元素

已采纳

Here is example code:

package main

import (
    "fmt"
)

type Product struct {
    Id       int64
    Title    string
    AttrVals []string
}

type ProductAttrValView struct {
    Product
    Attr string
}

type ProductAttrVal struct {
    Attr    string
    Product int64
    Value   string
}

func main() {
    p := Product{Id: 1, Title: "test", AttrVals: []string{}}
    var prod *Product
    prodViews := []ProductAttrValView{
        ProductAttrValView{ Product: p, Attr: "text1" },
        ProductAttrValView{ Product: p, Attr: "text2" },
        ProductAttrValView{ Product: p, Attr: "text3" },
        ProductAttrValView{ Product: p, Attr: "text4" },
    }

    // collapse join View to Product with Attrs
    for _, pview := range prodViews {
        if prod == nil {
            prod = &pview.Product
            prod.AttrVals = make([]string, 0, len(prodViews))
        }
        if pview.Attr != "" {
            fmt.Printf("appending '%s' to %p
", pview.Attr, prod) // output for debug
            prod.AttrVals = append(prod.AttrVals, pview.Attr)
        }
    }
    fmt.Printf("%+v
", prod) // output for debug

}

http://play.golang.org/p/949w5tYjcH

Here i have some returned data from DB in ProductAttrValView struct and want put it into Product struct and also fill Product.AttrVals

It prints:

&{Id:1 Title:test AttrVals:[text4]}

While i expect this:

&{Id:1 Title:test AttrVals:[text1 text2 text3 text4]}

So, all text should be appended, but for some reason only the last element stays in Attrs slice.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

  • dongquepao8653 dongquepao8653 7年前

    You are re-using variables in your for-range loop, and each iteration modifies the value of that same variable. You can create a new value each iteration with the trick:

    pview := pview
    

    http://play.golang.org/p/qtJXxdtuq2

    You also initialize the slice with a length of 4, but you append another value (ignoring the first 4). You likely meant to set the capacity of the slice as opposed to the length:

    prod.AttrVals = make([]string, 0, len(prodViews))
    

    Because the value of prod is changing each iteration, the code would be a lot less confusing if you specifically initialized the prod value, instead of assigning the address of &pview.Product

    prod = &Product{AttrVals: make([]string, 0, len(prodViews))}
    

    [time line]

    1. You create a single product p, containing an initialized []string
    2. That same p is assigned to all prodViews that we will iterate over.
    3. On the first iteration through the loop, you assign *prod to that initial p value, then change AttrVals to a new []string of length 4. This doesn't alter the original p.
    4. pview.Attr is appended to prod.AttrVals, making a it length 5, and creating a new underlying array. This isn't reflected in the values of p.
    5. On subsequent iterations, pview is overwritten with the next value in prodViews. This overwrites the value of prod too, since it points to the pview.Product value. This means that prod.AttrVals is now the same one from the original p.
    6. pview.Attr is appended to a slice of length 0, and the underlying array is replaced with more capacity, so pview.Attr still isn't contained in the original p.
    7. pview is again overwritten with the next value, which contains original p values, setting your AttrVals length back to 0 with an empty array.
    8. The cycle continues until the final single value is printed.
    点赞 评论 复制链接分享

为你推荐