I'm trying to copy the contents of a map ( amap
) inside another one (aSuperMap
) and then clear amap
so that it can take new values on the next iteration/loop.
The issue is that you can't clear the map without to clear its reference in the supermap as well.
Here is some pseudo code.
for something := range fruits{
aMap := make(map[string]aStruct)
aSuperMap := make(map[string]map[string]aStruct)
for x := range something{
aMap[x] = aData
aSuperMap[y] = aMap
delete(aMap, x)
}
//save aSuperMap
saveASuperMap(something)
}
I've also tried some dynamic stuff but obviously it throws an error (cannot assign to nil)
aSuperMap[y][x] = aData
The question is how can I create an associative map ? In PHP I simply use aSuperMap[y][x] = aData. It seems that golang doesn't have any obvious method. If I delete delete(aMap, x)
its reference from the super map is deleted as well. If I don't delete it the supermap ends up with duplicate data. Basically on each loop it gets aMap
with the new value plus all the old values.
This question is related to
go
I'd use recursion just in case so you can deep copy the map
and avoid bad surprises in case you were to change a map
element that is a map
itself.
Here's an example in a utils.go:
package utils
func CopyMap(m map[string]interface{}) map[string]interface{} {
cp := make(map[string]interface{})
for k, v := range m {
vm, ok := v.(map[string]interface{})
if ok {
cp[k] = CopyMap(vm)
} else {
cp[k] = v
}
}
return cp
}
And its test file (i.e. utils_test.go):
package utils
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestCopyMap(t *testing.T) {
m1 := map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
}
m2 := CopyMap(m1)
m1["a"] = "zzz"
delete(m1, "b")
require.Equal(t, map[string]interface{}{"a": "zzz"}, m1)
require.Equal(t, map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
}, m2)
}
It should easy enough to adapt if you need the map
key to be something else instead of a string
.
Individual element copy, it seems to work for me with just a simple example.
maps := map[string]int {
"alice":12,
"jimmy":15,
}
maps2 := make(map[string]int)
for k2,v2 := range maps {
maps2[k2] = v2
}
maps2["miki"]=rand.Intn(100)
fmt.Println("maps: ",maps," vs. ","maps2: ",maps2)
As stated in seong's comment:
Also see http://golang.org/doc/effective_go.html#maps. The important part is really the "reference to underlying data structure". This also applies to slices.
However, none of the solutions here seem to offer a solution for a proper deep copy that also covers slices.
I've slightly altered Francesco Casula's answer to accommodate for both maps and slices.
This should cover both copying your map itself, as well as copying any child maps or slices. Both of which are affected by the same "underlying data structure" issue. It also includes a utility function for performing the same type of Deep Copy on a slice directly.
Keep in mind that the slices in the resulting map will be of type []interface{}
, so when using them, you will need to use type assertion to retrieve the value in the expected type.
Example Usage
copy := CopyableMap(originalMap).DeepCopy()
Source File (util.go
)
package utils
type CopyableMap map[string]interface{}
type CopyableSlice []interface{}
// DeepCopy will create a deep copy of this map. The depth of this
// copy is all inclusive. Both maps and slices will be considered when
// making the copy.
func (m CopyableMap) DeepCopy() map[string]interface{} {
result := map[string]interface{}{}
for k,v := range m {
// Handle maps
mapvalue,isMap := v.(map[string]interface{})
if isMap {
result[k] = CopyableMap(mapvalue).DeepCopy()
continue
}
// Handle slices
slicevalue,isSlice := v.([]interface{})
if isSlice {
result[k] = CopyableSlice(slicevalue).DeepCopy()
continue
}
result[k] = v
}
return result
}
// DeepCopy will create a deep copy of this slice. The depth of this
// copy is all inclusive. Both maps and slices will be considered when
// making the copy.
func (s CopyableSlice) DeepCopy() []interface{} {
result := []interface{}{}
for _,v := range s {
// Handle maps
mapvalue,isMap := v.(map[string]interface{})
if isMap {
result = append(result, CopyableMap(mapvalue).DeepCopy())
continue
}
// Handle slices
slicevalue,isSlice := v.([]interface{})
if isSlice {
result = append(result, CopyableSlice(slicevalue).DeepCopy())
continue
}
result = append(result, v)
}
return result
}
Test File (util_tests.go
)
package utils
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestCopyMap(t *testing.T) {
m1 := map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
"c": []interface{} {
"d", "e", map[string]interface{} {
"f": "g",
},
},
}
m2 := CopyableMap(m1).DeepCopy()
m1["a"] = "zzz"
delete(m1, "b")
m1["c"].([]interface{})[1] = "x"
m1["c"].([]interface{})[2].(map[string]interface{})["f"] = "h"
require.Equal(t, map[string]interface{}{
"a": "zzz",
"c": []interface{} {
"d", "x", map[string]interface{} {
"f": "h",
},
},
}, m1)
require.Equal(t, map[string]interface{}{
"a": "bbb",
"b": map[string]interface{}{
"c": 123,
},
"c": []interface{} {
"d", "e", map[string]interface{} {
"f": "g",
},
},
}, m2)
}
You have to manually copy each key/value pair to a new map
. This is a loop that people have to reprogram any time they want a deep copy of a map
.
You can automatically generate the function for this by installing mapper
from the maps
package using
go get -u github.com/drgrib/maps/cmd/mapper
and running
mapper -types string:aStruct
which will generate the file map_float_astruct.go
containing not only a (deep) Copy
for your map but also other "missing" map
functions ContainsKey
, ContainsValue
, GetKeys
, and GetValues
:
func ContainsKeyStringAStruct(m map[string]aStruct, k string) bool {
_, ok := m[k]
return ok
}
func ContainsValueStringAStruct(m map[string]aStruct, v aStruct) bool {
for _, mValue := range m {
if mValue == v {
return true
}
}
return false
}
func GetKeysStringAStruct(m map[string]aStruct) []string {
keys := []string{}
for k, _ := range m {
keys = append(keys, k)
}
return keys
}
func GetValuesStringAStruct(m map[string]aStruct) []aStruct {
values := []aStruct{}
for _, v := range m {
values = append(values, v)
}
return values
}
func CopyStringAStruct(m map[string]aStruct) map[string]aStruct {
copyMap := map[string]aStruct{}
for k, v := range m {
copyMap[k] = v
}
return copyMap
}
Full disclosure: I am the creator of this tool. I created it and its containing package because I found myself constantly rewriting these algorithms for the Go map
for different type combinations.
Source: Stackoverflow.com