如何复制地图?


72

我正在尝试将地图(amap)的内容复制到另一个地图()内aSuperMap,然后清除, amap以便它可以在下一次迭代/循环中获取新值。问题是,如果不清除超级地图中的引用,也无法清除地图。这是一些伪代码。

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)

}

我也尝试了一些动态的东西,但显然它会引发错误(无法分配为nil)

aSuperMap[y][x] = aData

问题是如何创建关联地图?在PHP中,我只使用aSuperMap [y] [x] = aData。似乎golang没有任何明显的方法。如果我delete(aMap, x)从超级地图中删除了 它的引用,那么它也会被删除。如果我不删除它,超级地图最终将出现重复数据。基本上,在每个循环中,它将获得aMap新值加上所有旧值。

Answers:


121

您不是在复制地图,而是在复制地图参考。delete因此,您将同时修改原始地图和超级地图中的值。要复制地图,您必须使用如下for循环:

for k,v := range originalMap {
  newMap[k] = v
}

这是现已退休的SO文档中的示例:

// 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
}

摘录自地图-复制地图。最初的作者是JepZ。归属详细信息可以在贡献者页面上找到。该来源已获得CC BY-SA 3.0的许可,可以在文档档案中找到。参考主题ID:732和示例ID:9834。


4
另请参阅http://golang.org/doc/effective_go.html#maps。重要的部分实际上是“对基础数据结构的引用”。这也适用于切片。
2014年

1
谢谢!我虽然使用了(for循环),但是我担心这有点过头了,仍然会认为。
没有帽子的用户

似乎我还是在转圈。我想新的地图newMap [k]将被重载,因此我不能在下一个循环中真正清空它,而又不从超级地图中释放值。
不戴帽子的用户

如果originalMap是map [map [string] interface {}],其元素具有相同的JSON结构,则在后续迭代中,newMap [0]中的原始值将被originalMap [1]的key:value对覆盖。还使用originalMap [1]的key:value对创建了newMap [1]。对这种行为有解释吗?怎样才能实现相同?
Somesh 2015年

1
请注意,v只是一个副本,因此,如果您正在执行newMap [k] =&v之类的操作,它将无法正常工作(它将始终在循环中返回最后一个值)。万无一失的方式是newMap [k] =&originalMap [k]
Alekc

20

我会使用递归,以防万一,以便您可以深度复制,map并避免出现意外情况,以防您要更改本身就是一个map元素map

这是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
}

及其测试文件(即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)
}

如果您需要map密钥是其他东西而不是,它应该足够容易适应string


3
某人多久发布一次带有测试文件的解决方案!谢谢。来到这里进行深度复制。
Umang

因为切片可以具有相同的效果,所以这还不应该包括切片检测吗?
弥敦道(Nathan F.)

2

您必须手动将每个键/值对复制到新的map。这是一个循环,人们在需要深层复制的任何时候都必须重新编程map

您可以通过以下方式mappermaps软件包中安装,从而自动生成此功能:

go get -u github.com/drgrib/maps/cmd/mapper

并运行

mapper -types string:aStruct

这将生成文件map_float_astruct.go不仅包含一个(深)Copy为您的地图,还包括其他“失踪”map的功能ContainsKeyContainsValueGetKeys,和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
}

全面披露:我是这个工具的创造者。我创建了它及其包含的包,因为我发现自己不断地为Go重写这些算法以适应map不同的类型组合。


1

单个元素的复制,似乎对我来说只是一个简单的例子。

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)

1

诚如成诚的评论所述

另请参阅http://golang.org/doc/effective_go.html#maps。重要的部分实际上是“对基础数据结构的引用”。这也适用于切片。

但是,这里的任何解决方案似乎都无法为适当的深层复制(也包括切片)提供解决方案。

我略微更改了Francesco Casula的答案,以适合地图和切片。


这应该包括复制地图本身以及复制任何子地图或切片。两者都受到相同的“基础数据结构”问题的影响。它还包括一个实用程序功能,可直接在片上执行相同类型的Deep Copy。

请记住,结果映射中的切片将为类型[]interface{},因此在使用它们时,您将需要使用类型断言来检索所需类型中的值。

用法示例

copy := CopyableMap(originalMap).DeepCopy()

源文件(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
}

测试文件(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)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.