我有以下代码,如果它已经不存在,则会向切片添加新元素。如果它确实存在,那么qty属性应该增加现有元素而不是添加新元素:
package main
import (
"fmt"
)
type BoxItem struct {
Id int
Qty int
}
type Box struct {
BoxItems []BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// If the item exists already then increment its qty
for _, item := range box.BoxItems {
if item.Id == boxItem.Id {
item.Qty++
return item
}
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
func main() {
boxItems := []BoxItem{}
box := Box{boxItems}
boxItem := BoxItem{Id: 1, Qty: 1}
// Add this item 3 times its qty should be increased to 3 afterwards
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
fmt.Println(len(box.BoxItems)) // Prints 1 which is correct
for _, item := range box.BoxItems {
fmt.Println(item.Qty) // Prints 1 when it should print 3
}
}
问题是qty永远不会正确递增。它总是以1结尾,在提供的例子中它应该是3。
我已经调试了代码,看起来已经达到了增量部分,但是值并没有持久保存到项目中。
这里有什么问题?
答案 0 :(得分:5)
您正在Qty
的副本中递增box.BoxItems
,因为range
将生成切片中元素的副本。请参阅this example。
因此,在for _, item := range box.BoxItems
中,item
是box.BoxItems中元素的副本。
将循环更改为
for i := 0; i < len(box.BoxItems); i++ {
if box.boxItems[i].Id == boxItem.Id {
box.boxItems[i].Qty++
return box.BoxItems[i]
}
}
答案 1 :(得分:3)
我会像其他人一样回答你的问题。但是,并不是通过循环一系列值来解决您尝试解决的问题。请继续阅读:
正如其他人所说,for-range
在值范围内提供了不可变的迭代。这意味着您对迭代中提供的值所做的任何更改都将丢失。它基本上是给你一个真实值的副本,而不是实际值。
for _, item := range box.BoxItems {
// ^-not the real `item`, it's a copy!
解决此问题的方法是跟踪for idx, val := range
中的索引值,并使用此idx
来解决您直接查找的值。
如果更改for循环以保持索引值:
for i, item := range box.BoxItems {
// ^-keep this
您将能够引用您循环的数组中的实际项目:
for i, item := range box.BoxItems {
// Here, item is a copy of the value at box.BoxItems[i]
if item.Id == boxItem.Id {
// Refer directly to an item inside the slice
box.BoxItems[i].Qty++
return box.BoxItems[i] // Need to return the actual one, not the copy
}
}
我赞成这种方法而不是for i; i<Len; i++
,因为我发现它更具可读性。但这只是一个品味问题,for i
形式会更有效率(谨防过早优化!)。
您要做的是,如果BoxItems
已经存在,请避免重复Id
。为此,您将遍历box.BoxItems
切片的整个范围。如果您的N
切片中有box.BoxItems
项,则可能会在找到您要查找的项目不存在之前迭代所有N
个项目!基本上,这意味着您的算法是O(N)。
Id
即0, 1, 2, 3, ..., n - 1, n
,您可以继续使用切片来索引您的盒子项目。你会这样做:
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// Lookup your item by Id
if boxItem.Id < len(box.BoxItems) {
// It exists, do don't create it, just increment
item := box.BoxItems[boxItem.Id]
item.Qty++
box.BoxItems[boxItem.Id] = item
return item
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
Id
您应该使用提供快速查找的数据结构,例如内置map
,它提供O(1)查找(这意味着,您需要执行单个操作才能找到您的项目,而不是{ {1}}操作)。
n
这是解决问题的更正确方法。
答案 2 :(得分:1)
在index, value := range someSlice
中,value
是someSlice[index]
的全新副本。
package main
import (
"fmt"
)
type BoxItem struct {
Id int
Qty int
}
type Box struct {
BoxItems []BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// If the item exists already then increment its qty
for i := range box.BoxItems {
item := &box.BoxItems[i]
if item.Id == boxItem.Id {
item.Qty++
return *item
}
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
func main() {
boxItems := []BoxItem{}
box := Box{boxItems}
boxItem := BoxItem{Id: 1, Qty: 1}
// Add this item 3 times its qty should be increased to 3 afterwards
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
fmt.Println(len(box.BoxItems)) // Prints 1 which is correct
for _, item := range box.BoxItems {
fmt.Println(item.Qty) // Prints 1 when it should print 3
}
}
输出:
1
3