1

I would like to create multiple slices from one array. One slice each with one element missing. I am using this code (playground):

nums := []int{1,2,3}
for i := 0; i < len(nums); i++ {
    fmt.Println(append(nums[:i], nums[i+1:]...))
}

I was expecting to receive

[2 3]
[1 3]
[1 2]

But instead I receive

[2 3]
[2 3]
[2 3]

Why is that?

jub0bs
  • 60,866
  • 25
  • 183
  • 186
User12547645
  • 6,955
  • 3
  • 38
  • 69
  • So, I guess I am always appending to `nums`. But how do I prevent that? – User12547645 Jul 11 '21 at 14:13
  • `append(nums[:i], nums[i+1:]...)` modifies the values *inside* the slice's underlying array if they can fit, so the first iteration changes the `nums` underlying array to be `[2 3 3]`, and so on. – mkopriva Jul 11 '21 at 14:15
  • 1
    I guess this will help you https://stackoverflow.com/questions/35920534/why-does-append-modify-passed-slice – Carlos Martinez Jul 11 '21 at 14:16

2 Answers2

1

Thank you very much for pointing this out to me. This is my current solution. It simply copies the array on which I am working (playground)

package main

import (
    "fmt"
)

func main() {
    nums := []int{1,2,3}
        for i := 0; i < len(nums); i++ {
            cpy := make([]int, len(nums))
            copy(cpy, nums)
            fmt.Println(append(cpy[:i], cpy[i+1:]...))
        }
}
User12547645
  • 6,955
  • 3
  • 38
  • 69
1

As mkopriva correctly pointed out, the output of your program differs from what you expect because all slices resulting from append share the same underlying array. You need to somehow decouple those slices from the underlying array of slice nums.

One viable approach is indeed to use copy, as you did in your answer. There is a more concise alternative, although it may not be clearer to untrained eyes.

Rather than resorting to copy, you can use nums[:i:i] (a full-slice expression, a.k.a. three-index slice) as the first argument of append:

nums := []int{1,2,3}
for i := 0; i < len(nums); i++ {
  fmt.Println(append(nums[:i:i], nums[i+1:]...))
}

The resulting slice will be decoupled from slice nums's underlying array and you'll get the desired output:

[2 3]
[1 3]
[1 2]

(Playground)

Since Go 1.18, you can use functions Clone and Delete from the golang.org/x/exp/slices package to achieve the same result in an arguably more readable fashion:

func main() {
  nums := []int{1, 2, 3}
  for i := 0; i < len(nums); i++ {
    fmt.Println(slices.Delete(slices.Clone(nums), i, i+1))
  }
}

(Playground)

jub0bs
  • 60,866
  • 25
  • 183
  • 186