将一个映射的所有元素复制到另一个映射中

huangapple go评论94阅读模式
英文:

Copying all elements of a map into another

问题

给定

var dst, src map[K]V

我可以通过以下方式将src中的所有条目复制到dst

for k, v := range src {
    dst[k] = v
}

有没有更符合惯用方式的方法?

copy只适用于切片(以及作为源的string)。

英文:

Given

var dst, src map[K]V

I can copy all entries from src into dst by doing

for k, v := range src {
    dst[k] = v
}

Is there a more idiomatic way to do this?

copy only works on slices (and string as a source).

答案1

得分: 45

这对我来说看起来是一个完全合适的方法。我认为将一个地图复制到另一个地图中并不常见,所以没有一个一行代码的解决方案。

英文:

That looks like a perfectly fine way to do this to me. I don't think copying one map into another is common enough to have a one-liner solution.

答案2

得分: 10

使用简单的for range循环是最高效的解决方案。

请注意,内置的copy函数不能简单地将src的内存复制到dst的地址,因为它们可能具有完全不同的内存布局。映射会根据存储在其中的项目数量进行扩展。因此,例如,如果您有一个包含一百万个元素的映射,它占用的内存比新创建的映射要多得多,因此内置的copy函数不能只是复制内存而不分配新的内存。

如果您的映射很大,可以通过创建具有足够大容量的目标映射来加快元素复制的速度,以避免重新哈希和重新分配(初始容量不限制其大小),例如:

dst := make(map[K]V, len(src))

for k, v := range src {
    dst[k] = v
}

从Go 1.18开始,我们可以创建一个通用的解决方案:

func MapCopy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
    for k, v := range src {
        dst[k] = v
    }
}

进行测试:

m1 := map[int]string{1: "one", 2: "two"}
m2 := map[int]string{}
MapCopy(m2, m1)
fmt.Println(m2)

m3 := map[string]int{"one": 1, "two": 2}
m4 := map[string]int{}
MapCopy(m4, m3)
fmt.Println(m4)

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

map[1:one 2:two]
map[one:1 two:2]

此复制函数也可以在golang.org/x/exp/maps.Copy()中找到。


1.18之前的“通用”解决方案如下:

如果性能不是问题(例如,您正在使用小型映射),可以使用reflect包创建一个通用解决方案:

func MapCopy(dst, src interface{}) {
    dv, sv := reflect.ValueOf(dst), reflect.ValueOf(src)

    for _, k := range sv.MapKeys() {
        dv.SetMapIndex(k, sv.MapIndex(k))
    }
}

此解决方案不会检查参数是否真的是映射,并且目标是否不为nil。输出结果相同,请在Go Playground上尝试。

英文:

Using a simple for range loop is the most efficient solution.

Note that a builtin copy could not just copy the memory of src to the address of dst because they may have entirely different memory layout. Maps grow to accommodate the number of items stored in them. So for example if you have a map with a million elements, it occupies a lot more memory than a freshly created new map, and so a builtin copy could not just copy memory without allocating new.

If your map is big, you can speed up copying elements if you may create the destination map having a big-enough capacity to avoid rehashing and reallocation (the initial capacity does not bound its size), e.g.:

dst := make(map[K]V, len(src))

for k, v := range src {
    dst[k] = v
}

Starting with Go 1.18, we can create a generic solution:

func MapCopy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
    for k, v := range src {
        dst[k] = v
    }
}

Testing it:

m1 := map[int]string{1: "one", 2: "two"}
m2 := map[int]string{}
MapCopy(m2, m1)
fmt.Println(m2)

m3 := map[string]int{"one": 1, "two": 2}
m4 := map[string]int{}
MapCopy(m4, m3)
fmt.Println(m4)

Output (try it on the Go Playground):

map[1:one 2:two]
map[one:1 two:2]

This copy function is also available at golang.org/x/exp/maps.Copy().


Pre 1.18 "generic" solution follows:

If performance is not an issue (e.g. you're working with small maps), a general solution may be created using the reflect package:

func MapCopy(dst, src interface{}) {
    dv, sv := reflect.ValueOf(dst), reflect.ValueOf(src)

    for _, k := range sv.MapKeys() {
        dv.SetMapIndex(k, sv.MapIndex(k))
    }
}

This solution does not check if the arguments are really maps and if the destination is not nil. This outputs the same, try it on the Go Playground.

答案3

得分: 2

你可以使用github.com/linkosmos/mapop

input :=  map[string]interface{}{
  "Key1": 2,
  "key3": nil,
  "val": 2,
  "val2": "str",
  "val3": 4,
}

input2 := map[string]interface{}{
  "a2": "str",
  "a3": 4,
}

input = mapop.Merge(input, input2)

input{"Key1": 2, "key3": nil, "val": 2, "val2": "str", "val3": 4, "a2": "str", "a3": 4}
英文:

You could use github.com/linkosmos/mapop

input :=  map[string]interface{}{
  "Key1": 2,
  "key3": nil,
  "val": 2,
  "val2": "str",
  "val3": 4,
}

input2 := map[string]interface{}{
  "a2": "str",
  "a3": 4,
}

input = mapop.Merge(input, input2)

input{"Key1": 2, "key3": nil, "val": 2, "val2": "str", "val3": 4, "a2": "str", "a3": 4}

答案4

得分: 2

使用通用函数maps.Copy

> Copy函数将src中的所有键值对复制到dst中。当src中的键在dst中已经存在时,dst中的值将被src中的键关联的值覆盖。

使用方法如下:

package main

import (
	"fmt"

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

func main() {

	src := map[int]string{200: "foo", 300: "bar"}
	dest := map[int]string{}

	maps.Copy(dest, src)
	fmt.Println(dest) // map[200:foo 300:bar]

	dest2 := map[int]string{200: "will be overwritten"}
	maps.Copy(dest2, src)
	fmt.Println(dest2) // map[200:foo 300:bar]
}

Playground: https://go.dev/play/p/of_H-YaEtir

请注意,maps包位于golang.org/x/exp/maps中,这仍然是一个实验性的包,即不在Go的兼容性保证之内。希望它将来能够被移入标准库中。

如果您不想导入exp包,该函数可以使用Go 1.18的类型参数轻松编写。以下代码与maps源代码相同:

func Copy[M ~map[K]V, K comparable, V any](dst, src M) {
	for k, v := range src {
		dst[k] = v
	}
}

<hr>

在撰写本文时,正在讨论向Copy函数添加两个映射类型参数,使其变为如下形式:

func Copy[M1, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
	for k, v := range src {
		dst[k] = v
	}
}

这在功能上是相同的,因为类型参数KV对于M1M2都是相同的,但它使函数签名与maps包中的其他函数保持一致。

英文:

Go 1.18

Use the generic function maps.Copy

> Copy copies all key/value pairs in src adding them to dst. When a key in src is already present in dst, the value in dst will be overwritten by the value associated with the key in src.

You use it as such:

package main

import (
	&quot;fmt&quot;

	&quot;golang.org/x/exp/maps&quot;
)

func main() {

	src := map[int]string{200: &quot;foo&quot;, 300: &quot;bar&quot;}
	dest := map[int]string{}

	maps.Copy(dest, src)
	fmt.Println(dest) // map[200:foo 300:bar]

	dest2 := map[int]string{200: &quot;will be overwritten&quot;}
	maps.Copy(dest2, src)
	fmt.Println(dest2) // map[200:foo 300:bar]
}

Playground: https://go.dev/play/p/of_H-YaEtir

Note that the maps package is in golang.org/x/exp/maps, which is still experimental — i.e. outside of Go compatibility guarantee. Hopefully it will be moved into the standard lib at some point in the near future.

If you don't want to import exp packages, the function can be trivially written with Go 1.18 type parameters. The following code is identical to the maps source:

func Copy[M ~map[K]V, K comparable, V any](dst, src M) {
	for k, v := range src {
		dst[k] = v
	}
}

<hr>

At the time of writing, there is ongoing discussion to add two map type parameters to the Copy function, so it would become like this:

func Copy[M1, M2 ~map[K]V, K comparable, V any](dst M1, src M2) {
	for k, v := range src {
		dst[k] = v
	}
}

This is functionally the same thing, as the type parameters K and V are the same for both M1 and M2 but brings the signature in line with the other functions in the maps package.

huangapple
  • 本文由 发表于 2011年9月16日 04:08:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/7436864.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定