goslicego-map

Golang: Appending keys from a map to a slice of slices


I ran into this simple Golang code and was surprised by Go's behavior here. Can someone explain what is going on here, and how to write the below code correctly?

As you can see, I have a map, where the key is an array of int. I add a couple of values and then I loop through the map, convert each key to a slice and append each key to an object of type [][]int.

func test() {
    myMap := make(map[[3]int]bool)
    myMap[[3]int{1, 2, 3}] = true
    myMap[[3]int{0, 5, 4}] = true
    myMap[[3]int{9, 7, 1}] = true
    myMap[[3]int{0, 2, 8}] = true
    array := [][]int{}

    for val := range myMap {
        array = append(array, val[:])
    }
    fmt.Println(array)
}

I was expecting the last line to print [[1,2,3], [0,5,4], [9,7,1], [0,2,8]], however, to my surprise it prints [[0 2 8] [0 2 8] [0 2 8] [0 2 8]], or [[9 7 1] [9 7 1] [9 7 1] [9 7 1]], or some other variation containing only one of the keys multiple times.

My go version is 1.16.5


Solution

  • In a for-loop, the loop variables are overwriten at every iteration. That is, the val is an array, and for each iteration, the contents of val are overwritten with the next item in the map. Since you added slices (which are simply views over an array), all the slices have val as the backing array, and they all have the same contents, namely, whatever the last element iterated.

    To fix, copy the array:

        for val := range myMap {
            val:=val
            array = append(array, val[:])
        }