开发者

Copying all elements of a map into another

开发者 https://www.devze.com 2023-04-05 17:44 出处:网络
Given var dst, src map[K]V I can copy all entries from src into dst by doing for开发者_C百科 k, v := range src {

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消