英文:
How to update map values in Go
问题
我想要构建一个具有字符串键和结构值的映射,通过该映射我可以通过键更新映射中的结构值。
我尝试了以下代码(playground):
func main() {
dataReceived := []Data{
Data{ID: "D1", Value: "V1"},
Data{ID: "D2", Value: "V2"},
Data{ID: "D3", Value: "V3"},
Data{ID: "D4", Value: "V4"},
Data{ID: "D5", Value: "V5"},
}
dataManaged := map[string]Data{}
for _, v := range dataReceived {
fmt.Println("Received ID:", v.ID, "Value:", v.Value)
dataManaged[v.ID] = v
}
fmt.Println()
for m, n := range dataManaged {
n.Value = "UpdatedData for " + n.ID
fmt.Println("Data key:", m, "Value:", n.Value)
}
fmt.Println()
for o, p := range dataManaged {
fmt.Println("Data key:", o, "Value:", p.Value)
}
}
还有这个代码(playground),但它没有给出我想要的输出。
我真正想要的输出是:
Received ID: D1 Value: V1
Received ID: D2 Value: V2
Received ID: D3 Value: V3
Received ID: D4 Value: V4
Received ID: D5 Value: V5
Data key: D1 Value: UpdatedData for D1
Data key: D2 Value: UpdatedData for D2
Data key: D3 Value: UpdatedData for D3
Data key: D4 Value: UpdatedData for D4
Data key: D5 Value: UpdatedData for D5
Data key: D1 Value: UpdatedData for D1
Data key: D2 Value: UpdatedData for D2
Data key: D3 Value: UpdatedData for D3
Data key: D4 Value: UpdatedData for D4
Data key: D5 Value: UpdatedData for D5
英文:
I want to build a map with string key and struct value with which I'm able to update struct value in the map identified by map key.
I've tried this (playground):
func main() {
dataReceived := []Data{
Data{ID: "D1", Value: "V1"},
Data{ID: "D2", Value: "V2"},
Data{ID: "D3", Value: "V3"},
Data{ID: "D4", Value: "V4"},
Data{ID: "D5", Value: "V5"},
}
dataManaged := map[string]Data{}
for _, v := range dataReceived {
fmt.Println("Received ID:", v.ID, "Value:", v.Value)
dataManaged[v.ID] = v
}
fmt.Println()
for m, n := range dataManaged {
n.Value = "UpdatedData for " + n.ID
fmt.Println("Data key:", m, "Value:", n.Value)
}
fmt.Println()
for o, p := range dataManaged {
fmt.Println("Data key:", o, "Value:", p.Value)
}
}
and also this which doesn't give me desired output.
What I really want is this:
<pre><code>Received ID: D1 Value: V1
Received ID: D2 Value: V2
Received ID: D3 Value: V3
Received ID: D4 Value: V4
Received ID: D5 Value: V5
Data key: D1 Value: UpdatedData for D1
Data key: D2 Value: UpdatedData for D2
Data key: D3 Value: UpdatedData for D3
Data key: D4 Value: UpdatedData for D4
Data key: D5 Value: UpdatedData for D5
Data key: D1 Value: UpdatedData for D1
Data key: D2 Value: UpdatedData for D2
Data key: D3 Value: UpdatedData for D3
Data key: D4 Value: UpdatedData for D4
Data key: D5 Value: UpdatedData for D5</code></pre>
答案1
得分: 67
你不能直接更改映射中与键关联的值,只能重新分配值。
这给你留下了两种可能性:
-
在映射中存储指针,这样你就可以修改指向的对象(该对象不在映射数据结构内部)。
-
存储结构体值,但在修改时,你需要重新分配给键。
1. 使用指针
在映射中存储指针:dataManaged := map[string]*Data{}
当你“填充”映射时,不能使用循环的变量,因为它在每次迭代中都会被覆盖。相反,创建一个副本,并存储该副本的地址:
for _, v := range dataReceived {
fmt.Println("Received ID:", v.ID, "Value:", v.Value)
v2 := v
dataManaged[v.ID] = &v2
}
输出结果如预期。在Go Playground上尝试一下。
2. 重新分配修改后的结构体
继续在映射中存储结构体值:dataManaged := map[string]Data{}
遍历键值对将给你值的副本。因此,在修改值后,需要将其重新分配回去:
for m, n := range dataManaged {
n.Value = "UpdatedData for " + n.ID
dataManaged[m] = n
fmt.Println("Data key:", m, "Value:", n.Value)
}
在Go Playground上尝试这个例子。
英文:
You can't change values associated with keys in a map, you can only reassign values.
This leaves you 2 possibilities:
-
Store pointers in the map, so you can modify the pointed object (which is not inside the map data structure).
-
Store struct values, but when you modify it, you need to reassign it to the key.
1. Using pointers
Storing pointers in the map: dataManaged := map[string]*Data{}
When you "fill" the map, you can't use the loop's variable, as it gets overwritten in each iteration. Instead make a copy of it, and store the address of that copy:
for _, v := range dataReceived {
fmt.Println("Received ID:", v.ID, "Value:", v.Value)
v2 := v
dataManaged[v.ID] = &v2
}
Output is as expected. Try it on the Go Playground.
2. Reassigning the modified struct
Sticking to storing struct values in the map: dataManaged := map[string]Data{}
Iterating over the key-value pairs will give you copies of the values. So after you modified the value, reassign it back:
for m, n := range dataManaged {
n.Value = "UpdatedData for " + n.ID
dataManaged[m] = n
fmt.Println("Data key:", m, "Value:", n.Value)
}
Try this one on the Go Playground.
答案2
得分: 1
我正在学习Golang,谷歌把我带到了这里。一种方法是创建一个DataStore结构体。我想出了这个见此处。如果这是一个好的方法,请告诉我。
import (
"fmt"
)
type Data struct {
key string
value string
}
type DataStore struct {
datastore map[string]Data
}
func newDataStore() DataStore {
return DataStore{make(map[string]Data)}
}
/*
将键和值放入DataStore中。
如果键(k)已经存在,则用提供的值(v)替换。
*/
func (ds *DataStore) put(k, v string) {
dx := Data{key: k, value: v}
ds.datastore[k] = dx
}
/*
如果DataStore中存在键(k),则返回true。
*/
func (ds *DataStore) containsKey(k string) bool {
if _, ok := ds.datastore[k]; ok {
return ok
}
return false
}
/*
仅当键(k)在DataStore中不存在时,将键和值放入DataStore中。
如果put操作成功,则返回true;
如果键(k)已经存在于DataStore中,则返回false。
*/
func (ds *DataStore) putIfAbsent(k, v string) bool {
if val, ok := ds.datastore[k]; ok {
fmt.Println("datastore contains key: ", k, "with value =", val, " --- ", ok)
return false
}
fmt.Println("datastore does not contain ", k)
dx := Data{key: k, value: v}
ds.datastore[k] = dx
return true
}
/*
返回与键(k)关联的Data值。
*/
func (ds *DataStore) get(k string) Data {
return ds.datastore[k]
}
/*
删除给定键(k)的条目。
*/
func (ds *DataStore) removeKey(k string) {
delete(ds.datastore, k)
}
/*
删除给定键(k)的条目。
*/
func (ds *DataStore) removeKeys(k ...string) {
for _, d := range k {
delete(ds.datastore, d)
}
}
/*
打印键和值
*/
func (ds *DataStore) print() {
for k, v := range ds.datastore {
fmt.Println(k, " ", v)
}
}
func main() {
fmt.Println("Hello, playground")
ds := newDataStore()
ds.print()
ds.put("D1", "V1")
ds.put("D2", "V2")
ds.put("D3", "V3")
fmt.Println("datastore with initial values")
ds.print()
ds.put("D1", "UpdatedData for D1")
ds.put("D2", "UpdatedData for D2")
ds.put("D3", "UpdatedData for D3")
fmt.Println("datastore with updated values")
ds.print()
fmt.Println("datastore: putIfAbsent");
ds.putIfAbsent("D3", "Duplicate Key")
ds.putIfAbsent("D4", "V4")
ds.putIfAbsent("D5", "V5")
fmt.Println("datastore with new values")
result := ds.get("D1")
fmt.Println("fetching the value for D1: result: ", result)
testKey := "D4"
//testKeys := [2]string{"D5", "D2"}
fmt.Println("datastore: containsKey: ")
if ok := ds.containsKey(testKey); ok {
fmt.Println("has key ", testKey, ok)
} else {
fmt.Println("has no key ", testKey, ok)
}
ds.print()
ds.removeKey(testKey)
fmt.Println("afer removing ", testKey)
ds.print()
fmt.Println("afer removing ", "D5", "D1")
ds.removeKeys("D5", "D1")
ds.print()
}```
[1]: https://play.golang.org/p/4FEGkImcCKB
<details>
<summary>英文:</summary>
I am learning Golang and Google brought me here. One way is to create a DataStore struct. I came up with this [see here][1]. Do let me know if this is a good way to do.
```package main
import (
"fmt"
)
type Data struct {
key string
value string
}
type DataStore struct {
datastore map[string]Data
}
func newDataStore() DataStore {
return DataStore{make(map[string]Data)}
}
/*
Puts the key and value in the DataStore.
If the key (k) already exists will replace with the provided value (v).
*/
func (ds *DataStore) put(k, v string) {
dx := Data{key: k, value: v}
ds.datastore[k] = dx
}
/*
Returns true, if the DataStore has the key (k)
*/
func (ds *DataStore) containsKey(k string) bool {
if _, ok := ds.datastore[k]; ok {
return ok
}
return false
}
/*
Puts the key and value in the DataStore, ONLY if the key (k) is not present.
Returns true, if the put operation is successful,
false if the key (k) ia already present in the DataStore
*/
func (ds *DataStore) putIfAbsent(k, v string) bool {
if val, ok := ds.datastore[k]; ok {
fmt.Println("datastore contains key: ", k, "with value =", val, " --- ", ok)
return false
}
fmt.Println("datastore does not contain ", k)
dx := Data{key: k, value: v}
ds.datastore[k] = dx
return true
}
/*
Returns the Data value associated with the key (k).
*/
func (ds *DataStore) get(k string) Data {
return ds.datastore[k]
}
/*
Removes the entry for the given key(k)
*/
func (ds *DataStore) removeKey(k string) {
delete(ds.datastore, k)
}
/*
Removes the entry for the given key(k)
*/
func (ds *DataStore) removeKeys(k ...string) {
for _, d := range k {
delete(ds.datastore, d)
}
}
/*
Prints the keys and values
*/
func (ds *DataStore) print() {
for k, v := range ds.datastore {
fmt.Println(k, " ", v)
}
}
func main() {
fmt.Println("Hello, playground")
ds := newDataStore()
ds.print()
ds.put("D1", "V1")
ds.put("D2", "V2")
ds.put("D3", "V3")
fmt.Println("datastore with initial values")
ds.print()
ds.put("D1", "UpdatedData for D1")
ds.put("D2", "UpdatedData for D2")
ds.put("D3", "UpdatedData for D3")
fmt.Println("datastore with updated values")
ds.print()
fmt.Println("datastore: putIfAbsent");
ds.putIfAbsent("D3", "Duplicate Key")
ds.putIfAbsent("D4", "V4")
ds.putIfAbsent("D5", "V5")
fmt.Println("datastore with new values")
result := ds.get("D1")
fmt.Println("fetching the value for D1: result: ", result)
testKey := "D4"
//testKeys := [2]string{"D5", "D2"}
fmt.Println("datastore: containsKey: ")
if ok := ds.containsKey(testKey); ok {
fmt.Println("has key ", testKey, ok)
} else {
fmt.Println("has no key ", testKey, ok)
}
ds.print()
ds.removeKey(testKey)
fmt.Println("afer removing ", testKey)
ds.print()
fmt.Println("afer removing ", "D5", "D1")
ds.removeKeys("D5", "D1")
ds.print()
}```
[1]: https://play.golang.org/p/4FEGkImcCKB
</details>
# 答案3
**得分**: 0
关于结构体:在`range`循环中,变量是一个副本。当它不是指针时,对值的任何更改都不会反映在存储在映射中的值上。
除了其他解决方案之外,您可以将值更改为指针`map[string]*Data`,仅遍历键并更新键对应的值:
```go
for k := range dataManaged {
dataManaged[k].Value = "UpdatedData for " + dataManaged[k].ID
fmt.Println("Data key:", k, "Value:", dataManaged[k].Value)
}
如果映射值不是结构体,您可以利用零值。例如:一个映射T -> number
,并计算一个键在数组或切片中出现的次数:
data := []int{0,0,0,1,1,2,3,3,3,3,4}
m := make(map[int]int, 0)
for _, val := range data {
m[val] = m[val] + 1
}
它的工作原理是:如果键不存在,m[val]
会产生映射值类型的“零”形式。对于数值类型,这是0
。然后我加上1
,再次存储在槽位m[val]
中。这对于第一个值来说是完美的。
当键不存在时会发生什么:
1. m[val] = m[val] + 1
2. m[val] = 0 + 1
3. m[val] = 1
当键存在时会发生什么:
1. m[val] = m[val] + 1
2. m[val] = 1 + 1
3. m[val] = 2
英文:
About structs: the variables in range
loop are a copy. When it is not a pointer, any change to the value does not reflect on the value stored in the map.
Adding to other solutions, you can change values to pointer map[string]*Data
, range only keys and update the keyed value:
for k := range dataManaged {
dataManaged[k].Value = "UpdatedData for " + dataManaged[k].ID
fmt.Println("Data key:", k, "Value:", dataManaged[k].Value)
}
If the map value is not a struct, you can use zero value to your advantage. Example: a map T -> number
and count how many times a key occurs in an array, or slice:
data := []int{0,0,0,1,1,2,3,3,3,3,4}
m := make(map[int]int, 0)
for _, val := range data {
m[val] = m[val] + 1
}
How it works: if the key is absent, m[val]
produces the 'zero' form of the type of the map value. With numerical values, this is 0
. Then I add 1
, and store again in the slot m[val]
. This is perfect as the first value.
What happens when key does not exist:
1. m[val] = m[val] + 1
2. m[val] = 0 + 1
3. m[val] = 1
What happens when key exists:
1. m[val] = m[val] + 1
2. m[val] = 1 + 1
3. m[val] = 2
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论