How to copy a map?
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.
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.
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
.
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
Maps (Will automatically deep-copy slices as well)
copy := CopyableMap(originalMap).DeepCopy()
Slices (Will automatically deep-copy maps as well)
copy := CopyableSlice(originalSlice).DeepCopy()
Source
deepcopy.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 (deepcopy_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)
}