追加后修改Golang映射值

问题描述 投票:0回答:3

有人可以解释为什么我的结构体的值“Name”在附加到新切片后会发生变化吗?

package main

import (
    "fmt"
    "strings"

    "golang.org/x/text/language"
)

func main() {
    type Staff struct {
        Name     map[language.Tag]string
        Function string
    }
    var s = []Staff{
        Staff{
            Name:     map[language.Tag]string{language.Japanese: "Mike/Tom"},
            Function: "Cashier",
        },
    }
    checkedStaff := []Staff{}
    // check for multiple staff in one role
    for _, staff := range s {
        if !strings.Contains(staff.Name[language.Japanese], "/") {
            checkedStaff = append(checkedStaff, staff)
            continue
        }
        split := strings.Split(staff.Name[language.Japanese], "/")
        for _, staffNameNative := range split {
            staff.Name[language.Japanese] = strings.TrimSpace(staffNameNative)
            checkedStaff = append(checkedStaff, staff)
            fmt.Printf("%+v", checkedStaff)
        }
    }

}

我希望切片

checkedStaff
包含这些值:
[{Name:map[ja:Mike] Function:Cashier} {Name:map[ja:Tom] Function:Cashier}]

但它包含

[{Name:map[ja:Tom] Function:Cashier} {Name:map[ja:Tom] Function:Cashier}]

因此追加后,切片

checkedStaff
中的名称被修改。

参见Go Playground上的代码: https://go.dev/play/p/ovKO4Hc7N3t

go slice
3个回答
1
投票

相同效果的更简单演示:

m1 := map[string]int{"key": 0}
m2 := m1
m2["key"] = 1
fmt.Println(m1) // prints "map[key:1]"

[游乐场链接]

原因是,当您复制地图时,您实际上只是复制指向同一地图的指针,而不是创建单独的地图。

在您的例子中,您正在复制一个包含地图的结构。复制结构体会复制其每个字段,这基本上就是您想要的,除了复制映射字段不会创建单独的映射的问题。

要复制地图,您可以使用

maps.Clone
[doc link]:

copyOfStaff := staff
copyOfStaff.Name = maps.Clone(staff.Name)
copyOfStaff.Name[language.Japanese] = strings.TrimSpace(staffNameNative)
checkedStaff = append(checkedStaff, copyOfStaff)

[游乐场链接]


1
投票

好吧,让我们稍微简化一下你的程序(Go Playground):

package main

import (
    "fmt"

    "golang.org/x/text/language"
)

func main() {
    type Staff struct {
        Name     map[language.Tag]string
        Function string
    }

    staff := Staff{
        Name:     map[language.Tag]string{},
        Function: "Cashier",
    }
    checkedStaff := []Staff{}
    split := []string{"Mike", "Tom"}
    for _, staffNameNative := range split {
        staff.Name[language.Japanese] = staffNameNative
        fmt.Printf(">> checkedStaff + staff: %+v + %+v\n", checkedStaff, staff)
        checkedStaff = append(checkedStaff, staff)
        fmt.Printf("checkedStaff: %+v\n", checkedStaff)
    }
}

那么,会发生什么?

在第一次执行中,您设置了

staff.Name[language.Japanese] = "Mike"
,因此
staff = {Name:map[ja:Mike] Function:Cashier}
,将其附加到
[]
,从而产生以
[{Name:map[ja:Mike] Function:Cashier}]
开头的行。

现在第二个循环执行了

staff.Name[language.Japanese] = "Tom"
。由于 map 是引用类型,因此您修改了
checkedStaff
中的值,现在是
[{Name:map[ja:Tom] Function:Cashier}]
,并将其附加到
checkedStaff
,因此该行以
[{Name:map[ja:Tom] Function:Cashier} {Name:map[ja:Tom] Function:Cashier}]
继续。

通常您不想修改正在处理的源结构。如果您改变某些东西(通过分割或修剪),不要将其放回原始结构,而是创建一个新结构。


0
投票

您直接修改指向

s
中的一个结构体的指针,然后将 reference 存储到该值两次。

创建一个新的、修改后的实例来追加

package main

import (
    "fmt"
    "strings"

    "golang.org/x/text/language"
)

func main() {
    type Staff struct {
        Name     map[language.Tag]string
        Function string
    }
    var s = []Staff{
        Staff{
            Name:     map[language.Tag]string{language.Japanese: "Mike/Tom"},
            Function: "Cashier",
        },
    }
    checkedStaff := []Staff{}
    // check for multiple staff in one role
    for _, staff := range s {
        if !strings.Contains(staff.Name[language.Japanese], "/") {
            c := Staff{
                Name:     staff.Name,
                Function: staff.Function,
            }
            checkedStaff = append(checkedStaff, c)
            continue
        }
        split := strings.Split(staff.Name[language.Japanese], "/")
        for _, staffNameNative := range split {
            c := Staff{
                Name:     map[language.Tag]string{language.Japanese: strings.TrimSpace(staffNameNative)},
                Function: staff.Function,
            }
            checkedStaff = append(checkedStaff, c)
            fmt.Printf("%+v\n", checkedStaff)
        }
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.