In Python, I have the following:
i = series.index(s) # standard Python list.index() function
tmp = series.pop(i)
blah = f(tmp)
series.append(tmp)
In converting this to Go, I am looking for a similar way of retrieving an item from a slice by index, doing something with it, then putting the original item at the end of my slice.
From here, I have arrived at the following:
i = Index(series, s) // my custom index function...
tmp, series = series[i], series[i+1:]
blah := f(tmp)
series = append(series, tmp)
But this fails at the end of lists:
panic: runtime error: slice bounds out of range
How would I idiomatically translate this slice.pop() into Go?
6 Answers
The "Cut" trick in the linked document does what you want:
xs := []int{1, 2, 3, 4, 5}
i := 0 // Any valid index, however you happen to get it.
x := xs[i]
xs = append(xs[:i], xs[i+1:]...)
// Now "x" is the ith element and "xs" has the ith element removed.
Note that if you try to make a one-liner out of the get-and-cut operations you'll get unexpected results due to the tricky behavior of multiple assignments in which functions are called before other expressions are evaluated:
i := 0
x, xs := xs[i], append(xs[:i], xs[i+1:]...)
// XXX: x=2, xs=[]int{2, 3, 4, 5}
You can work around by wrapping the element access operation in any function call, such as the identity function:
i := 0
id := func(z int) { return z }
x, xs := id(xs[i]), append(xs[:i], xs[i+1:]...)
// OK: x=1, xs=[]int{2, 3, 4, 5}
However, at that point it's probably more clear to use separate assignments.
For completeness, a "cut" function and its usage could look like this:
func cut(i int, xs []int) (int, []int) {
y := xs[i]
ys := append(xs[:i], xs[i+1:]...)
return y, ys
}
t, series := cut(i, series)
f(t)
series = append(series, t)
If you want to write a function that does pop() in a similar way to python then you'll have to pass in a pointer to the object so the object can be modified, as pop both returns the value and alters the list
func pop(alist *[]int) int {
f:=len(*alist)
rv:=(*alist)[f-1]
*alist=(*alist)[:f-1]
return rv
}
func main() {
n:=[]int{1,2,3,4,5}
fmt.Println(n)
last:=pop(&n)
fmt.Println("last is",last)
fmt.Printf("list of n is now %v\n", n)
You can declaretype intSlice []int and you can declare method pop() using that pointer receiver: func (l *intSlice) pop() int. Then you can call .pop() on instance of intSlice object. This becomes stylistically more similar to Python.
package main
import (
"fmt"
)
type intSlice []int
func (l *intSlice) pop() int {
length := len(*l)
lastEle := (*l)[length-1]
*l = (*l)[:length-1]
return lastEle
}
func main() {
mySlice := intSlice{1, 2, 3, 4, 5, 6}
popped := mySlice.pop()
fmt.Println(popped)
fmt.Println(mySlice)
popped = mySlice.pop()
fmt.Println(popped)
fmt.Println(mySlice)
}
Result:
6
[1 2 3 4 5]
5
[1 2 3 4]
I'm not sure there's a direct equivalent of "pop()" ... but you can do something like this:
Slices can be created with the built-in make function; this is how you create dynamically-sized arrays.
The make function allocates a zeroed array and returns a slice that refers to that array:
a := make([]int, 5) // len(a)=5To specify a capacity, pass a third argument to make:
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
See also:
I'd do something similar to what paulsm4 suggested:
package main
import (
"fmt"
)
func main() {
a := []int{1,2,3,4,5}
i,b := pop(a)
fmt.Println(i,b) // 5 [1 2 3 4]
}
func pop(a []int) (int,[]int) {
return a[len(a)-1],a[:len(a)-1]
}
Another option would be to create a function that takes a pointer to an int slice which modifies the argument to remove the last value and return it:
func pop(xs *[]int) int {
x := (*xs)[len(*xs)-1] // Store the last value to return.
*xs = (*xs)[:len(*xs)-1] // Remove the last value from the slice.
return x
}
xs := []int{1, 2, 3} // => xs=[1, 2, 3]
x := pop(&xs) // => xs=[1, 2], x=3