How to copy a map in golang?

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

How to copy a map in golang?

问题

我可以提供一个将地图分解为两个切片的示例:

func decomposeMap(m map[string]int) ([]string, []int) {
  var i uint
  l := len(m)
  keys, values := make([]string, l), make([]int, l)
  for keys[i], values[i] = range m {
    i++
  }
  return keys, values
}

但是我无法编写地图复制的代码:

func copyMap(m map[string]int) map[string]int {
  m2 := make(map[string]int, len(m))
  for id, m2[id] = range m {} // 错误 - id 未声明
  for id, m2[id] := range m {} // 错误,m2[id] 已经声明
  // id 在这里不应该可访问,它应该只存在于循环内部
  return m2
}

我可以将id声明为一个变量,但我不希望它在for循环外部可用。我该如何混合赋值和声明,例如:for id:=, m[id]= range m {}?这样它将在for循环内部声明索引,并且在外部不可访问?

英文:

I can offer decomposition of map to 2 slices example:

func decomposeMap(m map[string]int) ([]string, []int) {
  var i uint
  l := len(m)
  keys, values := make([]string, l), make([]int, l)
  for keys[i], values[i] = range m {
    i++
  }
  return keys, values
}

but I am failing to write map copying:

func copyMap(m map[string]int) map[string]int {
  m2 := make(map[string]int, len(m))
  for id, m2[id] = range m {} // error - id is not declared
  for id, m2[id] := range m {} // error with m2[id] already declared
  // id should not be accessible here, it should exist only inside loop
  return m2
}

I can declare id as a var, but I dont want it to be available outside for loop. How can i mix assigment and declaration, eg: for id:=, m[id]= range m {} ?
So it will declare index just inside for loop, and will be not accessible outside?

答案1

得分: 6

id变量必须在for之前声明,因为你不能在m2[id]中使用短变量声明。

func copyMap(m map[string]int) map[string]int {
    m2 := make(map[string]int, len(m))
    var id string
    for id, m2[id] = range m {
    }
    return m2
}

**但是!这不会复制map!**在m2[id]被评估之前,只有将键分配给id,所以这个循环将为上一次迭代的键分配值,这不是复制,而是"洗牌"!

这基本上是一个元组赋值(键和值都被赋给id, m2[id])。规范:赋值

赋值分为两个阶段。首先,左边的索引表达式和指针间接引用(包括选择器中的隐式指针间接引用)以及右边的表达式都按照通常的顺序进行求值。其次,按从左到右的顺序执行赋值。

所以首先评估idm2[id](包括索引表达式),所以id还没有被改变,所以使用的是上一次迭代的值,只有在这之后才会分配新的键和值。

为了证明这一点,可以看一下:

m := map[string]int{
    "one":   1,
    "two":   2,
    "three": 3,
}
m2 := copyMap(m)
fmt.Println(m2)

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

map[:1 one:2 two:3]

值被分配给不同的键(与源map中的键不同),并且在第一次迭代中将一个值分配给空字符串键(即id的默认零值)。

要复制map,只需使用:

for id, v := range m {
    m2[id] = v
}

或者如果你想避免临时赋值:

for id := range m {
    m2[id] = m[id]
}
英文:

The id variable must be declared before for, because you can't use short variable declaration with m2[id].

func copyMap(m map[string]int) map[string]int {
	m2 := make(map[string]int, len(m))
	var id string
	for id, m2[id] = range m {
	}
	return m2
}

But! This won't duplicate the map! The key is only assigned to id after m2[id] is already evaluated, so this loop will assign values to keys of the previous iteration, this is not duplicating, this is "shuffling"!

This is basically a tuple assignment (key and value are assigned to id, m2[id]). Spec: Assignments:

> The assignment proceeds in two phases. First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order. Second, the assignments are carried out in left-to-right order.

So first id and m2[id] are evaluated (including the index expression), so id is not "yet" changed, so the value from the previous iteration is used, and only after this are the new key and values assigned.

To demonstrate, see:

m := map[string]int{
	"one":   1,
	"two":   2,
	"three": 3,
}
m2 := copyMap(m)
fmt.Println(m2)

Output (try it on the Go Playground):

map[:1 one:2 two:3]

The values are assigned to different keys (different than in the source map), and one value is assigned to the empty string key in the first iteration (the default, zero value of id).

To duplicate the map, simply use:

for id, v := range m {
	m2[id] = v
}

Or if you want to avoid the temporary assignment:

for id := range m {
	m2[id] = m[id]
}

huangapple
  • 本文由 发表于 2022年1月31日 19:31:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/70925245.html
匿名

发表评论

匿名网友

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

确定