我怎样才能把两张地图合并在一起?

我有一个递归函数,它创建表示文件路径的对象(键是路径,值是关于文件的信息)。它是递归的,因为它只用于处理文件,所以如果遇到一个目录,就会在该目录上递归地调用该函数。

尽管如此,我还是想在两个映射上执行相当于集合联合的操作(即使用递归调用中的值更新的“ main”映射)。除了在一个映射上迭代并将每个键、其中的值分配给另一个映射中的相同内容之外,还有什么惯用的方法可以做到这一点吗?

也就是说: 假设 a,bmap [string] *SomeObject类型的,并且 ab最终被填充,有没有办法用 b中的所有值来更新 a

120543 次浏览

在标准包中没有内置的方式或任何方法来进行这样的合并。

The idomatic way is to simply iterate:

for k, v := range b {
a[k] = v
}

如果您有两个嵌套的映射 leftright,那么这个函数将递归地将项从 right添加到 left。如果键已经在 left中,那么我们将递归到更深的结构中,只尝试 键到 left(例如,永远不要替换它们)。


type m = map[string]interface{}


// Given two maps, recursively merge right into left, NEVER replacing any key that already exists in left
func mergeKeys(left, right m) m {
for key, rightVal := range right {
if leftVal, present := left[key]; present {
//then we don't want to replace it - recurse
left[key] = mergeKeys(leftVal.(m), rightVal.(m))
} else {
// key not in left so we can just shove it in
left[key] = rightVal
}
}
return left
}

注意: 我不处理值本身不是 map[string]interface{}的情况。因此,如果你有 left["x"] = 1right["x"] = 2,那么上面的代码将恐慌时尝试 leftVal.(m)

Go 受到它是什么类型的地图的限制。我怀疑没有内置函数,因为映射可能存在无限多的类型声明。因此,您必须根据所使用的映射类型构建自己的 Merge 函数:

func MergeJSONMaps(maps ...map[string]interface{}) (result map[string]interface{}) {
result = make(map[string]interface{})
for _, m := range maps {
for k, v := range m {
result[k] = v
}
}
return result
}

Starting at go 1.18, thanks to the release of the 仿制药功能, there are now generic functions that union maps!

您可以使用类似 https://github.com/samber/lo的软件包来完成这项工作。 Note that the key can be of any "comparable" type, while the value can be of any type.

例如:

package main


import (
"fmt"
"github.com/samber/lo"
)


func main() {
map1 := map[string]interface{}{"k1": "v1", "k2": 2}
map2 := map[string]interface{}{"k2": "v2new", "k3": true}
map1 = lo.Assign(map1, map2)
fmt.Printf("%v", map1)
}

The result is:

map[k1:v1 k2:v2new k3:true]

由于 Go 1.18,您可以简单地使用 golang.org/x/exp/maps包中的 Copy功能:

package main


import (
"fmt"


"golang.org/x/exp/maps"
)


func main() {
src := map[string]int{
"one": 1,
"two": 2,
}
dst := map[string]int{
"two":   2,
"three": 3,
}
maps.Copy(dst, src)
fmt.Println("src:", src)
fmt.Println("dst:", dst)
}

(游乐场)

产出:

src: map[one:1 two:2]
dst: map[one:1 three:3 two:2]

One caveat of this approach is that your maps' key type must be 混凝土, i.e. not an interface type. For instance, the compiler won't allow you to pass values of type map[io.Reader]int to the Copy function:

package main


import (
"fmt"
"io"


"golang.org/x/exp/maps"
)


func main() {
var src, dst map[io.Reader]int
maps.Copy(dst, src)
fmt.Println("src:", src)
fmt.Println("dst:", dst)
}

(游乐场)

编译器输出:

go: finding module for package golang.org/x/exp/maps
go: downloading golang.org/x/exp v0.0.0-20220328175248-053ad81199eb
./prog.go:12:11: io.Reader does not implement comparable


Go build failed.

关于 proposal: spec: permit values to have type "comparable"中的这个限制的更多信息。