148
func main() {
    a := []string{"Hello1", "Hello2", "Hello3"}
    fmt.Println(a)
    // [Hello1 Hello2 Hello3]
    a = append(a[:0], a[1:]...)
    fmt.Println(a)
    // [Hello2 Hello3]
}

How does this delete trick with the append function work?

It would seem that it's grabbing everything before the first element (empty array)

Then appending everything after the first element (position zero)

What does the ... (dot dot dot) do?

Salvador Dali
  • 214,103
  • 147
  • 703
  • 753
Jorge Olivero
  • 3,705
  • 9
  • 27
  • 33

8 Answers8

290

Where a is the slice, and i is the index of the element you want to delete:

a = append(a[:i], a[i+1:]...)

... is syntax for variadic arguments in Go.

Basically, when defining a function it puts all the arguments that you pass into one slice of that type. By doing that, you can pass as many arguments as you want (for example, fmt.Println can take as many arguments as you want).

Now, when calling a function, ... does the opposite: it unpacks a slice and passes them as separate arguments to a variadic function.

So what this line does:

a = append(a[:0], a[1:]...)

is essentially:

a = append(a[:0], a[1], a[2])

Now, you may be wondering, why not just do

a = append(a[1:]...)

Well, the function definition of append is

func append(slice []Type, elems ...Type) []Type

So the first argument has to be a slice of the correct type, the second argument is the variadic, so we pass in an empty slice, and then unpack the rest of the slice to fill in the arguments.

flornquake
  • 3,156
  • 1
  • 21
  • 32
dave
  • 62,300
  • 5
  • 72
  • 93
  • 37
    Don't you get out of range exception if i is the last element from slice? ``a = append(a[:i], a[i+1:]...)`` – themihai May 21 '15 at 08:13
  • 5
    @DaveC I do get that error when working with my slices in my project :/ – Tyguy7 Dec 16 '16 at 17:34
  • 3
    @Tyguy7 from the spec: "For arrays or strings, the indices are in range if 0 <= low <= high <= len(a), otherwise they are out of range." Maybe in your case high < low; in that case you'll get the error. (https://golang.org/ref/spec#Slice_expressions) – mlg Jan 08 '17 at 10:04
  • 2
    How is the performance of this? I seriously hope it's not creating an entirely new slice under the hood.. – joonas.fi Feb 20 '17 at 18:28
  • 7
    @Tyguy7 I think you tried to delete elements of slice within a loop. So you have to be careful with indexes. – Nikolay Bystritskiy Jun 02 '17 at 06:26
  • 1
    @themihai I think Zyl's [answer](https://stackoverflow.com/a/53506009/3329384) clarifies this. – Nick P Jun 24 '20 at 13:44
45

There are two options:

A: You care about retaining array order:

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

B: You don't care about retaining order (this is probably faster):

a[i] = a[len(a)-1] // Replace it with the last one. CAREFUL only works if you have enough elements.
a = a[:len(a)-1]   // Chop off the last one.

See the link to see implications re memory leaks if your array is of pointers.

https://github.com/golang/go/wiki/SliceTricks

Chris
  • 39,719
  • 45
  • 189
  • 235
  • This is interesting, but not really answering the question – Bryan Jun 07 '16 at 14:54
  • This is nice, but it would've been better if it can delete the only element in the array – Naguib Ihab Jun 06 '18 at 21:12
  • 2
    Just a heads up, attempted to use the first one from b (replace with last element) doesn't obviously work if you're attempting to remove the last element in the slice lol – Allison Feb 14 '19 at 04:58
13

Rather than thinking of the indices in the [a:]-, [:b]- and [a:b]-notations as element indices, think of them as the indices of the gaps around and between the elements, starting with gap indexed 0 before the element indexed as 0.

enter image description here

Looking at just the blue numbers, it's much easier to see what is going on: [0:3] encloses everything, [3:3] is empty and [1:2] would yield {"B"}. Then [a:] is just the short version of [a:len(arrayOrSlice)], [:b] the short version of [0:b] and [:] the short version of [0:len(arrayOrSlice)]. The latter is commonly used to turn an array into a slice when needed.

Zyl
  • 2,690
  • 2
  • 22
  • 27
  • 1
    This helps to explain why the answer is "no" to themihai's [comment](https://stackoverflow.com/questions/25025409/delete-element-in-a-slice#comment48826785_25025536) on dave's [answer](https://stackoverflow.com/a/25025536/3329384), referring to [i+1:] even when referring to the ith element: https://play.golang.org/p/E0lQ3jPcjX5 – Nick P Jun 24 '20 at 13:42
5

... is syntax for variadic arguments.

I think it is implemented by the complier using slice ([]Type), just like the function append :

func append(slice []Type, elems ...Type) []Type

when you use "elems" in "append", actually it is a slice([]type). So "a = append(a[:0], a[1:]...)" means "a = append(a[0:0], a[1:])"

a[0:0] is a slice which has nothing

a[1:] is "Hello2 Hello3"

This is how it works

qaisjp
  • 722
  • 8
  • 31
frank.lin
  • 1,614
  • 17
  • 26
5

I'm getting an index out of range error with the accepted answer solution. Reason: When range start, it is not iterate value one by one, it is iterate by index. If you modified a slice while it is in range, it will induce some problem.

Old Answer:

chars := []string{"a", "a", "b"}

for i, v := range chars {
    fmt.Printf("%+v, %d, %s\n", chars, i, v)
    if v == "a" {
        chars = append(chars[:i], chars[i+1:]...)
    }
}
fmt.Printf("%+v", chars)

Expected :

[a a b], 0, a
[a b], 0, a
[b], 0, b
Result: [b]

Actual:

// Autual
[a a b], 0, a
[a b], 1, b
[a b], 2, b
Result: [a b]

Correct Way (Solution):

chars := []string{"a", "a", "b"}

for i := 0; i < len(chars); i++ {
    if chars[i] == "a" {
        chars = append(chars[:i], chars[i+1:]...)
        i-- // form the remove item index to start iterate next item
    }
}

fmt.Printf("%+v", chars)

Source: https://dinolai.com/notes/golang/golang-delete-slice-item-in-range-problem.html

timotew
  • 91
  • 1
  • 3
0

assuming the value of your slice is this: [9 3 7 11 4 12 16 19 4]

to delete the 7 and 11 element

x = append(x[:2], x[4:]...)

the result is now:

[9 4 12 16 19 4]

hope that helps!

Shahriar Ahmed
  • 502
  • 4
  • 11
ditot
  • 1
0
deletedIndex := 0
arr := []string{"0", "1", "2", "3", "4"}
before := arr[:deletedIndex]
after := arr[deletedIndex+1:]
data := append(before, after...)

Create two slices without deleted value and concat them

-1

Not an elegant approach but it works. (Using strings.Replace)

    var a = []string{"a", "b", "c", "d"}
    str := strings.Join(a, `,`)
    str2 := "a" // element to be removed

    arr := strings.Split(strings.Trim(strings.Replace(str, str2, "", -1), ","), ",")
bitsabhi
  • 728
  • 8
  • 12