Mehulkumar
Mehulkumar

Reputation: 866

How to update map values in Go

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:

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

Upvotes: 43

Views: 76657

Answers (3)

blackgreen
blackgreen

Reputation: 44608

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

Upvotes: 0

Lucas
Lucas

Reputation: 55

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.


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

Upvotes: 1

icza
icza

Reputation: 417472

You can't change values associated with keys in a map, you can only reassign values.

This leaves you 2 possibilities:

  1. Store pointers in the map, so you can modify the pointed object (which is not inside the map data structure).

  2. 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.

Upvotes: 71

Related Questions