获取两个地图的联合

jef*_*upp 57 union map go

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

所有这一切,我想在两个地图上做相当于集合的联合(即用递归调用的值更新的"主"地图).除了迭代一个地图并将每个键,其中的值分配给另一个地图中的相同内容之外,还有一种惯用的方法吗?

即:给定a,b的类型map [string] *SomeObject,以及ab最终被填充,有没有什么办法来更新a与所有的值b

ANi*_*sus 85

没有内置方式,也没有标准包中的任何方法来进行这样的合并.

简单的方法是简单地迭代:

for k, v := range b {
    a[k] = v
}
Run Code Online (Sandbox Code Playgroud)

  • 添加ANisus回答的内容:地图本质上是哈希表.可能没有任何方法可以比简单地迭代两个地图更快地计算两个地图的并集. (3认同)
  • 他希望联合地图,不一定是地图中的值。如果你想做类似的事情,你只需要将 `a[k] = v` 改为 `a[k] = a[k] + v` 或类似的东西。 (2认同)

jub*_*0bs 55

更新的答案

从Go 1.21开始,您可以简单地使用新maps.Copy函数

package main

import (
    "fmt"
    "maps"
)

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

游乐场

输出:

src: map[one:1 two:2]
dst: map[one:1 three:3 two:2]
Run Code Online (Sandbox Code Playgroud)

原答案

从 Go 1.18 开始,您可以简单地使用包中的Copy函数golang.org/x/exp/maps

package main

import (
    "fmt"

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

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

游乐场

输出:

src: map[one:1 two:2]
dst: map[one:1 three:3 two:2]
Run Code Online (Sandbox Code Playgroud)

这种方法的一个警告是,在 Go 版本 1.18.x 到 1.19.x 中,映射的键类型必须是具体的,即不是接口类型。例如,编译器不允许您将类型值传递map[io.Reader]intCopy函数:

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)
}
Run Code Online (Sandbox Code Playgroud)

编译器输出:

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.
Run Code Online (Sandbox Code Playgroud)

这个限制在 Go 1.20 (playground )中被取消


Ami*_*tch 11

从 go 1.18 开始,由于泛型功能的发布,现在有了联合映射的泛型函数!

您可以使用https://github.com/samber/lo之类的包来执行此操作。请注意,键可以是任何“可比较”类型,而值可以是任何类型。

例子:

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)
}
Run Code Online (Sandbox Code Playgroud)

结果是:

map[k1:v1 k2:v2new k3:true]
Run Code Online (Sandbox Code Playgroud)


JnB*_*ymn 6

如果您有几个嵌套的映射,left并且right,则此函数将递归地将项添加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
}
Run Code Online (Sandbox Code Playgroud)

注意:我不处理值本身不是 a 的情况map[string]interface{}。所以,如果你有left["x"] = 1right["x"] = 2,然后尝试在上面的代码就会死机leftVal.(m)