英文:
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
p
is assigned to allprodViews
that we will iterate over. - On the first iteration through the loop, you assign
*prod
to that initialp
value, then changeAttrVals
to a new[]string
of length 4. This doesn't alter the originalp
. pview.Attr
is 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,
pview
is overwritten with the next value inprodViews
. This overwrites the value ofprod
too, since it points to thepview.Product
value. This means thatprod.AttrVals
is now the same one from the originalp
. pview.Attr
is appended to a slice of length 0, and the underlying array is replaced with more capacity, sopview.Attr
still isn't contained in the originalp
.pview
is again overwritten with the next value, which contains originalp
values, setting yourAttrVals
length back to 0 with an empty array.- The cycle continues until the final single value is printed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论