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

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

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[:])
}

You're appending the loop iterator variable each time, which is updated each iteration. You need to append a locally-scoped copy instead:
for val := range myMap {
v := val
array = append(array, v[:])
}

Based on suggestion of Adrian, recreating your code with a simple program as follows:
package main
import (
"fmt"
)
func main() {
test()
}
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 {
key := val
array = append(array, key[:])
}
fmt.Println(array)
}
Output:
[[1 2 3] [0 5 4] [9 7 1] [0 2 8]]

Related

Can't modify a matrix in a function

I have an array of matrices and I try to mutate each matrix in case an if statement is true.
for example, if I have this matrix:
1 2 3
4 5 6
7 8 9
I want to change each odd number to 0.
This is what I have:
func main() {
matrices := createMatrix() <-- returns an array of matrices.
for _, matrix := range matrices {
removeOdds(matrix)
}
}
func removeOdds(mat [][]int) {
for i := 0; i < len(mat); i++ {
for j := 0; j < len(mat[i]); j++ {
if mat[i][j] % 2 != 0 {
mat[i][j] = 0
}
}
}
}
This is not working because the matrix is not being changed.
I read that Go pass array by value and not reference, so I tried using pointers. But still, when I print the matrix after the changes of removeOdds, I get the original one.
This is what I wrote:
func main() {
matrices := createMatrix() <-- returns an array of matrices.
for _, matrix := range matrices {
removeOdds(&matrix)
}
}
func removeOdds(mat *[][]int) {
for i := 0; i < len(*mat); i++ {
for j := 0; j < len((*mat)[i]); j++ {
if (*mat)[i][j] % 2 != 0 {
(*mat)[i][j] = 0
}
}
}
}
As far as I concerned, the code snippet looks completely ok.
To be clear, type []int is not an array, it's a slice. Array is a fix length block of data, and type signature of array should be like [3]int. Slice is a reference type, a variable length view onto the real data, means it does not own the data, it only record where to find the data in memory in its value.
When you pass a slice into function, that reference value is copied, even inside the function, you are still referencing the same data block, or you can say the underlaying array, as you are when out side function scope.
How ever, I've tried your code my self, and I wrote this:
type Mat = [][]int
func makeMat() Mat {
return [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
}
func main() {
mats := []Mat{}
for i := 0; i < 10; i++ {
mats = append(mats, makeMat())
}
for _, mat := range mats {
// no change was made to this function
removeOdds(mat)
}
for _, mat := range mats {
fmt.Println(mat)
}
}
output:
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
[[0 2 0] [4 0 6] [0 8 0]]
So I think there may be some mistake in your observation. Maybe provide more information about your createMatrix().
Your first approach is true except iterating over matrices.
You should use
for i := range matrices {
removeOdds(matrix[i])
}
instead of
for _, matrix := range matrices {
removeOdds(matrix)
}
https://go.dev/play/p/iE0uCE_6Z2v

Golang prepend the array [duplicate]

This question already has an answer here:
golang prepend a string to a slice ...interface{}
(1 answer)
Closed 4 years ago.
package main
import (
"fmt"
)
func main() {
var result [][]int
var tempArr []int
tempArr = append(tempArr, 1, 2, 3, 5)
result = append(result, tempArr)
prepend := []int{1, 2, 3}
result = append([]int{prepend}, result...) // Not working
fmt.Println(result)
}
What is the correct way to prepend to an array? I need help to fix this line:
result = append([]int{prepend}, result...)
The type is mismatch. []int{prepend} type is [] int. But prepend type is [] int. So []int{prepend} is not correct. The right way is [][]int{prepend}, below code will pass.
result = append([][]int{prepend}, result...)
The result will be:
[[1 2 3] [1 2 3 5]]

Working with maps in Golang

I am new to Go and doing a few exercises. One of them is to sort the numbers in an array by frequency, from most to least frequent.
Example:
Input: [2, 2, 5, 7, 4, 4, 4, 7, 2]
Output: [2, 4, 7, 5]
Note that [4, 2, 7, 5] would also be correct, since 4 and 2 have the same frequency.
For this purpose I am converting the array into a value value map, which here would look like this: [2:3][4:3][7:2][5:1] (2 and 3 have freq. of 3, 7 has the freq of 2,... )
Afterwards I would like to simply loop through the map and output the keys ordered by value. For that I use the following code, which apparently does not work. Why?
count := 0
max := -1
// break loop, if map is empty
for i := 0; i < 1; i-- {
if len(m) == 0 {
break
}
max = -1
// get key of biggest value
for k, v := range m {
if v > max {
max = k
}
}
// res (for result) is a slice of integers
res[count] = max
// remove key-value-pair from map
delete(m, max)
count++
}
return res
Please keep in mind that this is an exercise. I am very sure there are much better, build in ways to do this.
Your 'max' variable is meant to keep track of the maximum frequency seen so far. However when you do 'max = k' you're assigning a key.
You need to keep track of the maximum frequency and the key associated with that frequency in separate variables.
...
for k, v := range m {
if v > maxFreq {
maxFreq = v
mostFrequentKey = k
}
}
// res (for result) is a slice of integers
res[count] = mostFrequentKey
// remove key-value-pair from map
delete(m, mostFrequentKey)
count++
...
For sorted frequencies, use a map then a slice. For example,
package main
import (
"fmt"
"sort"
)
func main() {
Input := []int{2, 2, 5, 7, 4, 4, 4, 7, 2}
fmt.Println("Input: ", Input)
mFreq := make(map[int]int, len(Input))
for _, n := range Input {
mFreq[n]++
}
sFreq := make([][2]int, 0, len(mFreq))
for n, f := range mFreq {
sFreq = append(sFreq, [2]int{n, f})
}
sort.Slice(sFreq, func(i, j int) bool {
if sFreq[i][1] <= sFreq[j][1] {
if sFreq[i][1] < sFreq[j][1] {
return false
}
if sFreq[i][0] >= sFreq[j][0] {
return false
}
}
return true
},
)
Output := []int{2, 4, 7, 5}
fmt.Println("Output: ", Output)
fmt.Println("Frequencies:", sFreq)
}
Playground: https://play.golang.org/p/8tiSksz3S76
Output:
Input: [2 2 5 7 4 4 4 7 2]
Output: [2 4 7 5]
Frequencies: [[2 3] [4 3] [7 2] [5 1]]

Go: read bytes into array

I'm trying to append bytes to an array in the following way:
Go
func readBytes() {
b := make([]byte, 1)
a := [][]byte{}
for i := 0, i < 4, i++ {
conn.Read(b)
a = append(a, b)
fmt.Println(b)
}
fmt.Println(a)
}
Result from fmt.Println(b):
[2]
[5]
[5]
[3]
Result from fmt.Println(a):
[[3], [3], [3], [3]]
Why does it only print out the last byte sent?? Am I missing something?
b is a slice - and you're therefore updating the same underlying array each time you pass it to conn.Read. You can look at this Golang blog post to understand how this works.
Once you call fmt.Println(a) .. each slice is looking at the same underlying array.
You could fix this by instantiating the buffer b in the loop, or using an array instead of a slice.
Here's a working example that re-allocates the b slice inside the loop: http://play.golang.org/p/cN1BE8WSFE
It is essentially (with an int slice):
for i := 0; i < 5; i++ {
b = []int{i, i + 1, i + 2, i + 3, i + 4}
a = append(a, b)
}

Go variables being overwritten (bug?)

So bit of a weird one here. My question is, do people get the same results from running my code as I do? And if you do, is it a fault of my code (I'm a python programmer usually), or a bug in golang?
System info: Go version (1.1.2) linux x64 (fedora 19)
Background info on the code: What I'm doing is finding the highest cost route from the top of a triangle to the bottom, this is from project_euler 18 and 67
The bug: I set a variable called pathA, this is an integer list, plus a new int for the new value found from the triangle
e.g. 3, 7, 2 append 8 should equal 3, 2, 7, 8
and, it does! ... until I set pathB. pathB gets set correctly however suddenly pathA is the same value as pathB.
tl;dr one variable is being overwritten when I set another
My code is as follows:
package main
import (
"fmt"
)
func extendPaths(triangle, prePaths [][]int) [][]int {
nextLine := triangle[len(prePaths)]
fmt.Println("#####PrePaths: ", prePaths)
fmt.Println("#####nextLine: ", nextLine)
postPaths := [][]int{{}}
for i := 0; i < len(prePaths); i++ {
route := prePaths[i]
nextA := nextLine[i]
nextB := nextLine[i+1]
fmt.Println("Next A:", nextA, "Next B:", nextB, "\n")
pathA := append(route, nextA)
fmt.Println("pathA check#1:", pathA)
pathB := append(route, nextB)
fmt.Println("pathA check#2:", pathA, "\n")
postPaths = append(postPaths, pathA)
postPaths = append(postPaths, pathB)
}
postPaths = postPaths[1:]
prePaths = [][]int{postPaths[0]}
for i := 1; i < len(postPaths)-1; i += 2 {
if getSum(postPaths[i]) > getSum(postPaths[i+1]) {
prePaths = append(prePaths, postPaths[i])
} else {
prePaths = append(prePaths, postPaths[i+1])
}
}
prePaths = append(prePaths, postPaths[len(postPaths)-1])
return prePaths
}
func getSum(sumList []int) int {
total := 0
for i := 0; i < len(sumList); i++ {
total += sumList[i]
}
return total
}
func getPaths(triangle [][]int) {
prePaths := [][]int{{triangle[0][0]}}
for i := 0; i < len(triangle)-1; i++ {
prePaths = extendPaths(triangle, prePaths)
}
}
func main() {
triangle := [][]int{{3}, {7, 4}, {2, 4, 6}, {8, 5, 9, 3}}
getPaths(triangle)
}
This gives the output in my terminal shown below:
#####PrePaths: [[3]]
#####nextLine: [7 4]
Next A: 7 Next B: 4
pathA check#1: [3 7]
pathA check#2: [3 7]
#####PrePaths: [[3 7] [3 4]]
#####nextLine: [2 4 6]
Next A: 2 Next B: 4
pathA check#1: [3 7 2]
pathA check#2: [3 7 2]
Next A: 4 Next B: 6
pathA check#1: [3 4 4]
pathA check#2: [3 4 4]
#####PrePaths: [[3 7 2] [3 7 4] [3 4 6]]
#####nextLine: [8 5 9 3]
Next A: 8 Next B: 5
pathA check#1: [3 7 2 8]
pathA check#2: [3 7 2 5]
Next A: 5 Next B: 9
pathA check#1: [3 7 4 5]
pathA check#2: [3 7 4 9]
Next A: 9 Next B: 3
pathA check#1: [3 4 6 9]
pathA check#2: [3 4 6 3]
Here you can see that for the last 4 times that I set pathA, it is initially set correctly, but then gets overwritten by pathB.
Does anyone have any thoughts on this?
EDIT:
As pointed out by the comments below, what was needed was to make new slices and copy data from the originals. This was done using code from http://blog.golang.org/go-slices-usage-and-internals modified slightly:
func AppendInt(slice []int, data ...int) []int {
m := len(slice)
n := m + len(data)
if n > cap(slice) {
newSlice := make([]int, (n+1)*2)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:n]
copy(slice[m:n], data)
return slice
}
I also changed the code on the other side, where I created the slices pathA and pathB. This changed to:
for i := 0; i < len(prePaths); i++ {
nextA := nextLine[i]
nextB := nextLine[i+1]
pathA := AppendInt(prePaths[i], nextA)
pathB := AppendInt(prePaths[i], nextB)
postPaths = append(postPaths, pathA)
postPaths = append(postPaths, pathB)
}
EDIT2:
It's quite early in the morning here, and I flat out made a mistake in my first edit, I did not fully understand your solution, after a bit of hacking I got there in the end:
This code does not work (pathA gets overwritten):
for i := 0; i < len(prePaths); i++ {
nextA := nextLine[i]
nextB := nextLine[i+1]
pathA := append(prePaths[i], nextA)
pathB := append(prePaths[i], nextB)
postPaths = append(postPaths, pathA)
postPaths = append(postPaths, pathB)
}
This code also does not work (pathA gets overwritten):
for i := 0; i < len(prePaths); i++ {
newRoute := make([]int, len(prePaths[i]), (cap(prePaths[i])+1)*2)
copy(newRoute, prePaths[i])
nextA := nextLine[i]
nextB := nextLine[i+1]
pathA := append(newRoute, nextA)
pathB := append(newRoute, nextB)
postPaths = append(postPaths, pathA)
postPaths = append(postPaths, pathB)
}
However, if I mix the 2 scenarios above into the code below, it works fine (pathA does not get overwritten):
for i := 0; i < len(prePaths); i++ {
newRoute := make([]int, len(prePaths[i]), (cap(prePaths[i])+1)*2)
copy(newRoute, prePaths[i])
nextA := nextLine[i]
nextB := nextLine[i+1]
pathA := append(newRoute, nextA)
pathB := append(prePaths[i], nextB)
postPaths = append(postPaths, pathA)
postPaths = append(postPaths, pathB)
}
So, my solution was to make a copy of the array, and have them both use different ones.
A slice is basically a structure consisting of 3 things:
A pointer to an array of the elements in the slice
The length of that array (the "capacity")
The number of elements actually stored in the array (the "length")
When you run the following code:
append(x, element)
It does the following:
Check if extending the slice will exceed the capacity of the underlying array. If so, allocate a larger one and copy the existing elements to the new array, and update the capacity.
Write the new element (or elements) to the end of the array and update the length.
Return the new slice.
In your code, you have the following:
pathA := append(route, nextA)
pathB := append(route, nextB)
Now there are two possibilities here:
len(route) == cap(route), and a new backing array will be allocated, with pathA and pathB having independent values.
len(route) < cap(route), so pathA and pathB end up sharing the same backing array. The last element in the array will be nextB, since that operation was run second.
It seems that the first case is true for the first few iterations of your loop, after which you hit the second case. You could avoid this by manually making a copy for one of your paths (allocate a slice with make(), and then use copy() to copy the old data).

Resources