Golang remove elements when iterating over slice panics - go

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

Related

Merge pointer to a slice

playground
package main
import (
"fmt"
"math/rand"
)
func randoms() *[]int {
var nums []int = make([]int, 5, 5) //Created slice with fixed Len, cap
fmt.Println(len(nums))
for i := range [5]int{} {//Added random numbers.
nums[i] = rand.Intn(10)
}
return &nums//Returning pointer to the slice
}
func main() {
fmt.Println("Hello, playground")
var nums []int = make([]int, 0, 25)
for _ = range [5]int{} {//Calling the functions 5 times
res := randoms()
fmt.Println(res)
//nums = append(nums, res)
for _, i := range *res {//Iterating and appending them
nums = append(nums, i)
}
}
fmt.Println(nums)
}
I am trying to mimic my problem. I have dynamic number of function calls i.e randoms and dynamic number of results. I need to append all of the results i.e numbers in this case.
I am able to do this with iteration and no issues with it. I am looking for a way to do something like nums = append(nums, res). Is there any way to do this/any built-in methods/did I misunderstand the pointers?
I think you're looking for append(nums, (*res)...):
nums = append(nums, (*res)...)
playground
See this answer for more about ..., but in short it expands the contents of a slice. Example:
x := []int{1, 2, 3}
y := []int{4, 5, 6}
x = append(x, y...) // Now x = []int{1, 2, 3, 4, 5, 6}
Further, since you have a pointer to a slice, you need to dereference the pointer with *.
x := []int{1, 2, 3}
y := &x
x = append(x, (*x)...) // x = []int{1, 2, 3, 1, 2, 3}

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

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)
}

slice shift like function in go lang

how array shift function works with slices?
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
for k, v := range s {
x, a := s[0], s[1:] // get and remove the 0 index element from slice
fmt.Println(a) // print 0 index element
}
}
I found an example from slice tricks but can't get it right.
https://github.com/golang/go/wiki/SliceTricks
x, a := a[0], a[1:]
Edit can you please explain why x is undefined here?
Building upon the answer and merging with SliceTricks
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
fmt.Println(len(s), s)
for len(s) > 0 {
x, s = s[0], s[1:] // undefined: x
fmt.Println(x) // undefined: x
}
fmt.Println(len(s), s)
}
For example,
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
fmt.Println(len(s), s)
for len(s) > 0 {
x := s[0] // get the 0 index element from slice
s = s[1:] // remove the 0 index element from slice
fmt.Println(x) // print 0 index element
}
fmt.Println(len(s), s)
}
Output:
6 [2 3 5 7 11 13]
2
3
5
7
11
13
0 []
References:
The Go Programming Language Specification: For statements
Addendum to answer edit to question:
Declare x,
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
fmt.Println(len(s), s)
for len(s) > 0 {
var x int
x, s = s[0], s[1:]
fmt.Println(x)
}
fmt.Println(len(s), s)
}
Output:
6 [2 3 5 7 11 13]
2
3
5
7
11
13
0 []
You can copy and paste my code for any slice type; it infers the type for x. It doesn't have to be changed if the type of s changes.
for len(s) > 0 {
x := s[0] // get the 0 index element from slice
s = s[1:] // remove the 0 index element from slice
fmt.Println(x) // print 0 index element
}
For your version, the type for x is explicit and must be changed if the type of s is changed.
for len(s) > 0 {
var x int
x, s = s[0], s[1:]
fmt.Println(x)
}
Just a quick explanation on how we implement shift-like functionality Go. It's actually a very manual process. Take this example:
catSounds := []string{"meow", "purr", "schnurr"}
firstValue := stuff[0] // meow
catSounds = catSounds[1:]
On the first line, we create our slice.
On the second line we get the first element of the slice.
On the third line, we re-assign the value of catSounds to everything currently in catSounds after the first element (catSounds[1:]).
So given all that, we can condense the second and third lines with a comma for brevity:
catSounds := []string{"meow", "purr", "schnurr"}
firstValue, catSounds := catSounds[0], catSounds[1:]

Is there a way to iterate over a slice in reverse in Go?

It would be convenient to be able to say something like:
for _, element := reverse range mySlice {
...
}
Edit: I asked this question a long time ago, it is 2022 now and the generic solution by #Ivan below seems like the way to go!
No there is no convenient operator for this to add to the range one in place. You'll have to do a normal for loop counting down:
s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
fmt.Println(s[i])
}
You can also do:
s := []int{5, 4, 3, 2, 1}
for i := range s {
fmt.Println(s[len(s)-1-i]) // Suggestion: do `last := len(s)-1` before the loop
}
Output:
1
2
3
4
5
Also here: http://play.golang.org/p/l7Z69TV7Vl
Variation with index
for k := range s {
k = len(s) - 1 - k
// now k starts from the end
}
How about use defer:
s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
defer fmt.Println(s[i])
}
One could use a channel to reverse a list in a function without duplicating it. It makes the code nicer in my sense.
package main
import (
"fmt"
)
func reverse(lst []string) chan string {
ret := make(chan string)
go func() {
for i, _ := range lst {
ret <- lst[len(lst)-1-i]
}
close(ret)
}()
return ret
}
func main() {
elms := []string{"a", "b", "c", "d"}
for e := range reverse(elms) {
fmt.Println(e)
}
}
In 2022, you could use generics to reverse any slice in-place:
func reverse[S ~[]E, E any](s S) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
When I need to extract elements from a slice and reverse range, I use something like this code:
// reverse range
// Go Playground: https://play.golang.org/p/gx6fJIfb7fo
package main
import (
"fmt"
)
type Elem struct {
Id int64
Name string
}
type Elems []Elem
func main() {
mySlice := Elems{{Id: 0, Name: "Alice"}, {Id: 1, Name: "Bob"}, {Id: 2, Name: "Carol"}}
for i, element := range mySlice {
fmt.Printf("Normal range: [%v] %+v\n", i, element)
}
//mySlice = Elems{}
//mySlice = Elems{{Id: 0, Name: "Alice"}}
if last := len(mySlice) - 1; last >= 0 {
for i, element := last, mySlice[0]; i >= 0; i-- {
element = mySlice[i]
fmt.Printf("Reverse range: [%v] %+v\n", i, element)
}
} else {
fmt.Println("mySlice empty")
}
}
Output:
Normal range: [0] {Id:0 Name:Alice}
Normal range: [1] {Id:1 Name:Bob}
Normal range: [2] {Id:2 Name:Carol}
Reverse range: [2] {Id:2 Name:Carol}
Reverse range: [1] {Id:1 Name:Bob}
Reverse range: [0] {Id:0 Name:Alice}
Playground: https://play.golang.org/p/gx6fJIfb7fo
You can use the funk.ForEachRight method from go-funk:
results := []int{}
funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) {
results = append(results, x)
})
fmt.Println(results) // []int{4, 3, 2, 1}

Resources