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
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)
}
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
.
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.
You are not copying the map, but the reference to the map. Your delete
thus modifies the values in both your original map and the super map. To copy a map, you have to use a for
loop like this:
for k,v := range originalMap {
newMap[k] = v
}
Here's an example from the now-retired SO documentation:
// Create the original map
originalMap := make(map[string]int)
originalMap["one"] = 1
originalMap["two"] = 2
// Create the target map
targetMap := make(map[string]int)
// Copy from the original map to the target map
for key, value := range originalMap {
targetMap[key] = value
}
Excerpted from Maps - Copy a Map. The original author was JepZ. Attribution details can be found on the contributor page. The source is licenced under CC BY-SA 3.0 and may be found in the Documentation archive. Reference topic ID: 732 and example ID: 9834.
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)