Working with maps in Golang - sorting

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

Related

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

How to iterate on slices, 4 items at a time

How do I iterate through a Go slice 4 items at a time.
lets say I have [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
I want a for loop to be able to get
[1,2,3,4] //First Iteration
[5,6,7,8] //Second Iteration
[9,10,11,12] //Third Iteration
[13,14,15,] // Fourth Iteration
I can do this in java and python but for golang I really dont have an idea.
For example,
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
for i := 0; i < len(slice); i += 4 {
var section []int
if i > len(slice)-4 {
section = slice[i:]
} else {
section = slice[i : i+4]
}
fmt.Println(section)
}
}
Playground: https://play.golang.org/p/kf7_OJcP13t
Output:
[1 2 3 4]
[5 6 7 8]
[9 10 11 12]
[13 14 15]
How To Iterate on Slices in Go, Iterating 4 items at a time. I want a
for loop.
In Go, readability is paramount. First we read the normal path, then we read the exception/error paths.
We write the normal path.
n := 4
for i := 0; i < len(s); i += n {
ss := s[i : i+n]
fmt.Println(ss)
}
We use n for the stride value throughout.
We write a little tweak that doesn't disturb the normal path to handle an exception, the end of the slice.
if n > len(s)-i {
n = len(s) - i
}
For example,
package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
n := 4
for i := 0; i < len(s); i += n {
if n > len(s)-i {
n = len(s) - i
}
ss := s[i : i+n]
fmt.Println(ss)
}
}
Playground: https://play.golang.org/p/Vtpig2EeXB7
Output:
[1 2 3 4]
[5 6 7 8]
[9 10 11 12]
[13 14 15]

How to find an element intersect in other array

I have an array like:
a:= [1,2,3,4,5]
b:= [5,6,7,8,9]
How to know array b have contain element in array a without using foreach?
How to know array b have contain element in array a without using foreach?
You can't. And you should not try as this is pointless restriction.
If the arrays are sorted (as they appear to be in your question) there is an algorithm that works better than going through each element.
Pick the first element of a, call it x.
Binary search b for the first element equal or greater than x. If they are equal, you found an element that is contained in both arrays, if not, make that your new x. Now search a for x in the same way. Repeat until you run out of elements in one of the arrays.
This can be trivially extended to an arbitrary number of arrays (in fact, it's easier to write with an arbitrary number of arrays).
Here's a quick and dirty implementation:
package main
import (
"fmt"
"sort"
)
func inter(arrs ...[]int) []int {
res := []int{}
x := arrs[0][0]
i := 1
for {
off := sort.SearchInts(arrs[i], x)
if off == len(arrs[i]) {
// we emptied one slice, we're done.
break
}
if arrs[i][off] == x {
i++
if i == len(arrs) {
// x was in all the slices
res = append(res, x)
x++ // search for the next possible x.
i = 0
}
} else {
x = arrs[i][off]
i = 0 // This can be done a bit more optimally.
}
}
return res
}
func main() {
a := []int{1, 2, 3, 4, 5, 7}
b := []int{5, 6, 7, 8, 9}
fmt.Println(inter(a, b))
}
package main
import (
set "github.com/deckarep/golang-set"
)
func array_intersect(a, b []interface{}) []interface{} {
return set.NewSetFromSlice(a).Intersect(set.NewSetFromSlice(b)).ToSlice()
}
func main() {
a := []interface{}{1, 2, 3, 4, 5, 7}
b := []interface{}{5, 6, 7, 8, 9}
println(array_intersect(a, b))
}
package main
import (
"fmt"
"sort"
)
func array_intersect(a, b []int) []int {
ret := []int{}
lenA := len(a)
lenB := len(b)
if lenA == 0 || lenB == 0 {
return ret
}
sort.Ints(a)
sort.Ints(b)
var i, j int
for {
a = a[i:]
if i = sort.SearchInts(a, b[j]); i >= len(a) {
break
}
if a[i] == b[j] {
ret = append(ret, a[i])
}
if j++; j >= lenB {
break
}
}
return ret
}
func main() {
a := []int{5, 7, 1, 1, 2, 3, 4, 5, 7}
b := []int{1, 1, 5, 6, 7, 8, 9}
fmt.Printf("a=%v, b=%v", a, b)
fmt.Printf("%v\n", array_intersect(a, b))
fmt.Printf("a=%v, b=%v", a, b)
}

Golang remove elements when iterating over slice panics

I want delete some elements from a slice, and https://github.com/golang/go/wiki/SliceTricks advise this slice-manipulation:
a = append(a[:i], a[i+1:]...)
Then I coded below:
package main
import (
"fmt"
)
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for i, value := range slice {
if value%3 == 0 { // remove 3, 6, 9
slice = append(slice[:i], slice[i+1:]...)
}
}
fmt.Printf("%v\n", slice)
}
with go run hello.go, it panics:
panic: runtime error: slice bounds out of range
goroutine 1 [running]:
panic(0x4ef680, 0xc082002040)
D:/Go/src/runtime/panic.go:464 +0x3f4
main.main()
E:/Code/go/test/slice.go:11 +0x395
exit status 2
How can I change this code to get right?
I tried below:
1st, with a goto statement:
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
Label:
for i, n := range slice {
if n%3 == 0 {
slice = append(slice[:i], slice[i+1:]...)
goto Label
}
}
fmt.Printf("%v\n", slice)
}
it works, but too much iteration
2nd, use another slice sharing same backing array:
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
dest := slice[:0]
for _, n := range slice {
if n%3 != 0 { // filter
dest = append(dest, n)
}
}
slice = dest
fmt.Printf("%v\n", slice)
}
but not sure if this one is better or not.
3rd, from Remove elements in slice, with len operator:
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for i := 0; i < len(slice); i++ {
if slice[i]%3 == 0 {
slice = append(slice[:i], slice[i+1:]...)
i-- // should I decrease index here?
}
}
fmt.Printf("%v\n", slice)
}
which one should I take now?
with benchmark:
func BenchmarkRemoveSliceElementsBySlice(b *testing.B) {
for i := 0; i < b.N; i++ {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
dest := slice[:0]
for _, n := range slice {
if n%3 != 0 {
dest = append(dest, n)
}
}
}
}
func BenchmarkRemoveSliceElementByLen(b *testing.B) {
for i := 0; i < b.N; i++ {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for i := 0; i < len(slice); i++ {
if slice[i]%3 == 0 {
slice = append(slice[:i], slice[i+1:]...)
}
}
}
}
$ go test -v -bench=".*"
testing: warning: no tests to run
PASS
BenchmarkRemoveSliceElementsBySlice-4 50000000 26.6 ns/op
BenchmarkRemoveSliceElementByLen-4 50000000 32.0 ns/op
it seems delete all elements in one loop is better
Iterate over the slice copying elements that you want to keep.
k := 0
for _, n := range slice {
if n%3 != 0 { // filter
slice[k] = n
k++
}
}
slice = slice[:k] // set slice len to remaining elements
The slice trick is useful in the case where a single element is deleted. If it's possible that more than one element will be deleted, then use the for loop above.
working playground example
while this is good answer for small slice:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
k := 0
for _, n := range slice {
if n%3 != 0 { // filter
slice[k] = n
k++
}
}
slice = slice[:k]
fmt.Println(slice) //[1 2 4 5 7 8]
}
for minimizing memory write for first elements (for big slice), you may use this:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
k := 0
for i, n := range slice {
if n%3 != 0 { // filter
if i != k {
slice[k] = n
}
k++
}
}
slice = slice[:k]
fmt.Println(slice) //[1 2 4 5 7 8]
}
and if you need new slice or preserving old slice:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
s2 := make([]int, len(slice))
k := 0
for _, n := range slice {
if n%3 != 0 { // filter
s2[k] = n
k++
}
}
s2 = s2[:k]
fmt.Println(s2) //[1 2 4 5 7 8]
}

Why does range iterating with _ blank identifier produce different values

I'm learning Go and having a great time so far.
The following code outputs the sum as 45
package main
import "fmt"
func main(){
//declare a slice
numSlice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var sum int = 0
for num := range numSlice {
sum += num
fmt.Println("num =", num)
}
fmt.Println("sum =", sum)
}
The following code, where I use _ the blank identifier to ignore the index in the for declaration outputs the sum as 55
//declare a slice
numSlice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var sum int = 0
for _,num := range numSlice {
sum += num
fmt.Println("num =", num)
}
fmt.Println("sum =", sum)
This has got me slightly stumped. From my understanding the blank identifier is used to ignore the slice index . But it also seems to be shifting the index and thereby ignoring the last element in the slice.
Can you please explain what's happening here and possibly why. I'm assuming this is not a bug and is by design. Go is so well designed so what would the possible use cases be for this kind of behaviour?
Single parameter range uses indexes, not values. Because your indexes are also going up from 0 to 9 using range with a single param will add the indexes up from 0 to 9 and give you 45
package main
import "fmt"
func main(){
//declare a slice
numSlice := []int{0, 0, 0, 0}
var sum int = 0
for num := range numSlice {
sum += num
fmt.Println("num =", num)
}
fmt.Println("sum =", sum)
}
Output
num = 0
num = 1
num = 2
num = 3
sum = 6

Resources