1

如果切片尚不存在,我有以下代码将新元素添加到切片中。如果它确实存在,那么 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
    }
}

问题是数量永远不会正确增加。它总是以 1 结尾,而在提供的示例中它应该是 3。

我已经调试了代码,并且确实似乎达到了增量部分,但该值并未保留到项目中。

这里有什么问题?

4

3 回答 3

5

您正在增加Qty的副本,box.BoxItems因为range将产生切片中元素的副本。请参阅此示例

因此,infor _, item := range box.BoxItemsitembox.BoxItems 中元素的副本。

将循环更改为

for i := 0; i < len(box.BoxItems); i++ {
    if box.boxItems[i].Id == boxItem.Id {
         box.boxItems[i].Qty++
         return box.BoxItems[i]
    }
}

操场

于 2013-08-04T14:39:29.783 回答
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
    }
}

操场

I would favor this approach over the for i; i<Len; i++ one as I find it more readable. But this is simply a matter of taste and the for i form will be more efficient (beware of premature-optimization!).

Your real problem is

What you're trying to do is to avoid duplicating BoxItems if their Id already exists. To do this, you iterate over the whole range of the box.BoxItems slice. If you have N items in your box.BoxItems slice, you will potentially iterate over all N items before finding out that the item you're looking for doesn't exist! Basically, this means your algorithm is O(N).

If you increment Id in natural order

That is, 0, 1, 2, 3, ..., n - 1, n, you can keep using a slice to index your box items. You would do like this:

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
}

Playground

If you increment Id in any order

You should use a datastructure that offers fast lookups, such as the built-in map, which offers O(1) lookups (that means, you need to do a single operation to find your item, not n operations).

type Box struct {
    BoxItems map[int]BoxItem
}

func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {

    // Lookup the map by Id
    item, ok := box.BoxItems[boxItem.Id]
    if ok {
        // It exists, do don't create it, just increment
        item.Qty++
    } else {
        item = boxItem
    }

    // New item so add it to the map
    box.BoxItems[boxItem.Id] = item
    return item
}

Playground

This is a more correct way to solve your problem.

于 2013-08-05T04:42:10.100 回答
1

index, value := range someSlicevalue是全新的副本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
于 2013-08-04T14:39:39.050 回答