Reputation: 979
I'm getting some unexpected behavior when I try to loop through a slice and remove every element in sequence to print the remaining elements, using the suggested Delete method from SliceTricks. For example, when I try and loop through a slice that contains the letters [A B C]
, I expect the output to be [B C]
, [A C]
, [A B]
in that order:
Method 1
package main
import "fmt"
func main() {
a := []string {"A", "B", "C"}
for i, _ := range a {
fmt.Println(append(a[:i], a[i+1:]...))
}
}
However, the output here is surprising to me. It outputs [B C]
three times.
I did eventually get my expected behavior by doing the following:
Method 2
package main
import "fmt"
func main() {
a := []string {"A", "B", "C"}
for i, _ := range a {
result := make([]string, 0)
result = append(result, a[:i]...)
result = append(result, a[i+1:]...)
fmt.Println(result)
}
}
What is causing the unexpected behavior in method 1 and is there a better way to achieve this other than method 2?
Upvotes: 2
Views: 201
Reputation: 26370
If you want to do it without extra allocations, try just swapping out values as shown in this example:
Note that this assumes you are OK with shuffling the list contents. If not, just copy the list once before the loop and do the same as below.
package main
import "fmt"
func main() {
set := []string{"a", "b", "c"}
for i := range set {
set[0], set[i] = set[i], set[0]
fmt.Println(set[1:])
}
}
Output:
[b c]
[a c]
[a b]
Upvotes: 5
Reputation: 3828
The "what" is because append(slice, elems...)
is actually updating the slice
with the new elements. It returns a "new" slice only because it may have had to relocate the original slice due to memory reallocation. So, in your example code, you are actually changing the contents of a
with each call to append
. (A good overview is in the "Append: An Example" section of this golang blog post).
As to "how", my attempt is:
package main
import "fmt"
func main() {
a := []string {"A", "B", "C"}
for i, _ := range a {
result := make([]string, i, len(a)-1)
copy(result, a[:i])
result = append(result, a[i+1:]...)
fmt.Println(result)
}
}
Initializing result
with a length and capacity, as well as using copy
instead of two append
s attempts to reduce the number of memory reallocations required.
Upvotes: 1
Reputation: 17335
See what happens after the first iteration:
package main
import "fmt"
func main() {
a := []string{"A", "B", "C"}
x := append(a[:0], a[1:]...)
fmt.Println(a)
fmt.Println(x)
}
Gives
[B C C]
[B C]
See https://play.golang.org/p/2LqBwHejhy
The next two iterations are similar. Just try it out.
Upvotes: 0