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) 1If 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) 2You 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] } 2Another 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