英文:
Golang, appending leaves only last element
问题
这是示例代码:
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"},
}
// 将 View 中的数据合并到 Product 中的 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\n", pview.Attr, prod) // 调试输出
prod.AttrVals = append(prod.AttrVals, pview.Attr)
}
}
fmt.Printf("%+v\n", prod) // 调试输出
}
这段代码从数据库中返回了一些数据,存储在 ProductAttrValView 结构体中,然后将其放入 Product 结构体中,并填充 Product.AttrVals。
输出结果为:
&{Id:1 Title:test AttrVals:[text4]}
而期望的结果是:
&{Id:1 Title:test AttrVals:[text1 text2 text3 text4]}
所以,所有的文本应该被追加到 Attrs 切片中,但由于某种原因,只有最后一个元素保留在 Attrs 切片中。
英文:
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\n", pview.Attr, prod) // output for debug
prod.AttrVals = append(prod.AttrVals, pview.Attr)
}
}
fmt.Printf("%+v\n", 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
得分: 1
你在for-range循环中重复使用变量,并且每次迭代都修改了该变量的值。你可以使用以下技巧在每次迭代中创建一个新值:
pview := pview
http://play.golang.org/p/qtJXxdtuq2
你还使用长度为4的切片进行初始化,但是你使用append添加了另一个值(忽略了前4个)。你可能想设置切片的容量而不是长度:
prod.AttrVals = make([]string, 0, len(prodViews))
由于prod的值在每次迭代中都会改变,如果你明确初始化prod的值,而不是分配&pview.Product的地址,代码会更清晰易懂:
prod = &Product{AttrVals: make([]string, 0, len(prodViews))}
[时间线]
- 你创建了一个包含初始化的[]string的单个产品
p。 - 同一个
p被分配给我们将要迭代的所有prodViews。 - 在第一次循环迭代中,你将
*prod赋值为初始的p值,然后将AttrVals更改为一个新的长度为4的[]string。这不会改变原始的p。 pview.Attr被追加到prod.AttrVals,使其长度为5,并创建一个新的底层数组。这不会反映在p的值中。- 在后续的迭代中,
pview被下一个prodViews中的值覆盖。这也会覆盖prod的值,因为它指向pview.Product的值。这意味着prod.AttrVals现在与原始的p相同。 pview.Attr被追加到长度为0的切片中,并且底层数组被替换为更大的容量,所以pview.Attr仍然不包含在原始的p中。pview再次被下一个值覆盖,该值包含原始的p值,将你的AttrVals长度重置为0,并创建一个空数组。- 这个循环一直持续到打印最后一个单个值。
英文:
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]
- You create a single product
p, containing an initialized []string - That same
pis assigned to allprodViewsthat we will iterate over. - On the first iteration through the loop, you assign
*prodto that initialpvalue, then changeAttrValsto a new[]stringof length 4. This doesn't alter the originalp. pview.Attris appended toprod.AttrVals, making a it length 5, and creating a new underlying array. This isn't reflected in the values ofp.- On subsequent iterations,
pviewis overwritten with the next value inprodViews. This overwrites the value ofprodtoo, since it points to thepview.Productvalue. This means thatprod.AttrValsis now the same one from the originalp. pview.Attris appended to a slice of length 0, and the underlying array is replaced with more capacity, sopview.Attrstill isn't contained in the originalp.pviewis again overwritten with the next value, which contains originalpvalues, setting yourAttrValslength back to 0 with an empty array.- The cycle continues until the final single value is printed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论