在Golang中合并地图

6
我需要合并多个映射,map1 = [ id: id_1 val: val_1 ]map2 = [ id: id_2 val: val_2 ]map3 = [id: id_1, val: val_3],合并后的结果应该是基于id值进行合并的。
result_map = [id: id_1 val: {val_1, val_3}, id: id_2 var: {val_2}} ]

我尝试过的代码:
var a = make(map[string]interface{})
for _, m := range data {
    for _, n := range data {
        if m["id"] == n["id"] {
            for l, k := range n {
                c[l] = k
            }
        }
    }
}

有没有方法可以实现这个?我正在使用 Golang 1.7。

谢谢


2
你尝试了什么?遇到了什么问题?请包括你的代码、输出和/或错误信息,以及你期望得到的结果。 - Jonathan Hall
另外,停止使用Go 1.7,它已经不再得到支持。目前最新版本是1.10.2。 - icza
感谢您更新问题并附上代码。您在使用该代码时遇到了什么问题? - Jonathan Hall
1个回答

10

简单合并

是的,它们可以合并,但由于在结果映射中可能会有多个与同一键相关联的值,因此值的类型应该是一个切片,例如map[string][]string

要执行合并,只需遍历要合并的映射,并将源映射中的每个值附加到结果映射中与相同键相关联的切片中。

需要注意的一件事是,在执行附加操作后,必须将结果切片重新分配给结果映射中的同一键。

这是一个简单的实现:

func merge(ms ...map[string]string) map[string][]string {
    res := map[string][]string{}
    for _, m := range ms {
        for k, v := range m {
            res[k] = append(res[k], v)
        }
    }
    return res
}

这个merge()函数有一个可变参数,这意味着您可以将任意数量的地图传递给它。

请注意,您不需要初始化目标映射中的切片,因为使用尚未存在于其中的键索引映射将导致其类型的零值(切片的零值是nil),而您可以向nil切片附加,内置的append()函数会处理(重新)分配。

测试:

m1 := map[string]string{"id_1": "val_1"}
m2 := map[string]string{"id_2": "val_2"}
m3 := map[string]string{"id_1": "val_3"}

res := merge(m1, m2, m3)
fmt.Println(res)

输出结果(在Go Playground上试一下):

map[id_1:[val_1 val_3] id_2:[val_2]]

避免重复

请注意,上述的merge()不会过滤掉重复项,这意味着如果多个输入映射中包含相同的"id_1": "val_1"对,则在目标列表中它将出现多次,例如:"id_1": ["val_1", "val_1", "val_x"]。要过滤掉这样的重复项(只在目标列表中列出一次),我们需要在执行附加操作之前进行检查(如果之前已经遇到了该项,则跳过附加操作)。

以下是如何实现:

func merge(ms ...map[string]string) map[string][]string {
    res := map[string][]string{}
    for _, m := range ms {
    srcMap:
        for k, v := range m {
            // Check if (k,v) was added before:
            for _, v2 := range res[k] {
                if v == v2 {
                    continue srcMap
                }
            }
            res[k] = append(res[k], v)
        }
    }
    return res
}

测试一下:

m1 := map[string]string{"id_1": "val_1"}
m2 := map[string]string{"id_2": "val_2", "id_1": "val_1"}
m3 := map[string]string{"id_1": "val_3"}

res := merge(m1, m2, m3)
fmt.Println(res)

输出(在Go Playground上尝试):

map[id_1:[val_1 val_3] id_2:[val_2]]
我们可以看到 "id_1": "val_1"m1m2 中都被包含,但是值 "val_1" 只在目标映射中与 "id_1" 键相关联的切片中列出了一次。

它不能处理切片中的重复值,请尝试使用 https://github.com/fatih/set。 - CallMeLoki
@danicheeta 是的,重复的值将会列出多次。这可能是问题提问者想要的,但他并没有说明。 - icza
@user3809560 你想处理重复项吗?我的意思是,如果多个输入映射中都包含相同的“id_1”:“val_1”对,那么在目标中它会被列出多次,例如“id_1”:[“val_1”,“val_x”,“val_1”],这样可以吗?或者你想要过滤掉这些重复项? - icza
@icza,你在srcMap中使用了一个for循环。它是什么控制结构?它在1.7中支持吗? - user3809560
@user3809560 是的,它被Go 1.7支持(但你真的应该放弃Go 1.7并选择Go 1.10.2)。第二个版本中有3个循环。最外层循环遍历输入映射的切片(ms),第二个循环遍历输入映射(m),最内层循环遍历结果映射的切片值以检查重复项。 - icza
当再次遇到相同的键时,覆盖旧值而不是丢弃新值,这样做更有意义吧?在合并操作中,我希望最后一次出现的值能够保留。 - The Fool

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接