web-dev-qa-db-ja.com

マップのすべての要素を別の要素にコピーする

与えられた

var dst, src map[K]V

srcからdstにすべてのエントリをコピーできます

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

これを行うためのより慣用的な方法はありますか?

copyはスライスでのみ機能します(およびソースとしてstring)。

62
Mike Samuel

これは私にこれを行うための完全に素晴らしい方法のように見えます。あるマップを別のマップにコピーすることは、ワンライナーソリューションを実現するほど一般的ではないと思います。

46
Lily Ballard

単純なfor rangeループを使用するのが最も効率的なソリューションです。

組み込みのcopyは、srcのメモリをdstのアドレスにコピーすることはできません。メモリレイアウトがまったく異なる可能性があるためです。マップは、そこに保存されているアイテムの数に対応するために成長します。たとえば、100万個の要素を持つマップがある場合、新しく作成した新しいマップよりも多くのメモリを占有するため、組み込みのcopyは、新しいメモリを割り当てることなくメモリをコピーすることはできません。

マップが大きい場合、再ハッシュや再割り当てを避けるために十分な容量を持つデスティネーションマップを作成すると、要素のコピーを高速化できます(初期容量はサイズに制限されません):

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

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

パフォーマンスが問題にならない場合(たとえば、小さなマップで作業している場合)、 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でないかどうかをチェックしません。それをテストする:

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]
7
icza

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}
2
Ernestas Poskus