Copying all elements of a map into another
Given
var dst, src map[K]V
I can copy all entries from src
into dst
by doing
for开发者_C百科 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).
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.
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
}
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
. 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]
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}
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
}
}
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.
精彩评论