英文:
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
}
}
这在功能上是相同的,因为类型参数K
和V
对于M1
和M2
都是相同的,但它使函数签名与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 (
"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
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论