How to delete an element from a slice in Go

Removing an element from a slice is not particularly intuitive in Go. Unlike other languages, Go does not provide a built-in function for this purpose. If you’re wondering how an element may be removed from a Go slice, the code snippets below should provide the answers you seek.

Let’s consider a few strategies to remove elements from a slice in Go. The first two sections below assume that you want to modify the slice in place. If you want to create a copy of the slice with the element removed, while leave the original as is, please jump to the Preserve the original slice section below.

If slice order is unimportant

If the order of the elements in a slice is not important, you can copy the last element to the index that you want to remove, then truncate the slice up until the last element as shown below:

package main

import (
	"fmt"
)

// removeElement deletes the element at index `i`
// without preserving the order of the slice `s`.
// Note that the original slice is modified
func removeElement(s []int, i int) ([]int, error) {
	// s is [1,2,3,4,5,6], i is 2

	// perform bounds checking first to prevent a panic!
	if i >= len(s) || i < 0 {
		return nil, fmt.Errorf("Index is out of range. Index is %d with slice length %d", i, len(s))
	}

	// copy the last element (6) to index `i`. At this point,
	// `s` will be [1,2,6,4,5,6]
	s[i] = s[len(s)-1]
	// Remove the last element from the slice by truncating it
	// This way, `s` will now include all the elements from index 0
	// up to (but not including) the last element
	return s[:len(s)-1], nil
}

func main() {
	s := []int{1, 2, 3, 4, 5, 6}
	// Remove the element at the 2nd index (3)
	s, err := removeElement(s, 2)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(s) // [1,2,6,4,5]
	// The capacity of the slice is preserved
	// even though the length has changed
	fmt.Println(cap(s)) // 6
}

A variation of the above solution which is potentially more readable is to copy the first element to the specified index, then truncate the slice from the second index. A subtle difference with this solution compared to the previous one is that the capacity of the slice is not preserved which makes this method potentially less performant if you’ll be appending to the slice later.

package main

import (
	"fmt"
)

// removeElement deletes the element at index `i`
// without preserving the order of the slice `s`.
// Note that the original slice is modified
func removeElement(s []int, i int) ([]int, error) {
	// s is [1,2,3,4,5,6], i is 2

	// perform bounds checking first to prevent a panic!
	if i >= len(s) || i < 0 {
		return nil, fmt.Errorf("Index is out of range. Index is %d with slice length %d", i, len(s))
	}

	// copy first element (1) to index `i`. At this point,
	// `s` will be [1,2,1,4,5,6]
	s[i] = s[0]
	// Remove the first element from the slice by truncating it
	// This way, `s` will now include all the elements from index 1
	// until the element at the last index
	return s[1:], nil
}

func main() {
	s := []int{1, 2, 3, 4, 5, 6}
	// Remove the element at the 2nd index (3)
	s, err := removeElement(s, 2)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(s) // [2,1,4,5,6]
	// The capacity of the slice is reduced
	fmt.Println(cap(s)) // 5

In general, prefer the first approach to the second unless you’re sure you won’t be appending to the slice later.

If slice order is important

If you want to maintain the order of the elements in the slice, you can use the following technique known as re-slicing which leverages the built-in append function:

package main

import (
	"fmt"
)

// removeElement deletes the element at index `i`
// and preserves the order of the slice `s`.
// Note that the original slice is modified
func removeElement(s []int, i int) ([]int, error) {
	// s is [1,2,3,4,5,6], i is 2

	// perform bounds checking first to prevent a panic!
	if i >= len(s) || i < 0 {
		return nil, fmt.Errorf("Index is out of range. Index is %d with slice length %d", i, len(s))
	}

	// This creates a new slice by creating 2 slices from the original:
	// s[:i] -> [1, 2]
	// s[i+1:] -> [4, 5, 6]
	// and joining them together using `append`
	return append(s[:i], s[i+1:]...), nil
}

func main() {
	s := []int{1, 2, 3, 4, 5, 6}
	// Remove the element at the 2nd index (3)
	s, err := removeElement(s, 2)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(s) // [1,2,4,5,6]. 3 is removed but the original order is preserved
}

This method is more expensive than the first one because you’re joining two new slices together to create a third one using append as opposed to one assignment and one copy in the previous section.

Preserve the original slice

Both solutions discussed above modify the original slice in one way or another. If this is not desired, you need to create a brand new slice with a different underlying array before appending to it. Make sure to assign the result of removeElement to a new variable as well to maintain access to the original.

Here’s a variant of the re-slicing method that leaves the original slice passed to removeElement unmodified:

package main

import (
	"fmt"
)

// removeElement deletes the element at index `i`
// and preserves the order of the slice `s`.
// The original slice is not modified here
func removeElement(s []int, i int) ([]int, error) {
	// s is [1,2,3,4,5,6], i is 2

	// perform bounds checking first to prevent a panic!
	if i >= len(s) || i < 0 {
		return nil, fmt.Errorf("Index is out of range. Index is %d with slice length %d", i, len(s))
	}

	// Create a brand new slice first.
	// This slice has a different underlying array to `s`
	newSlice := make([]int, 0)

	// copy the elements in `s` to `newSlice`
	// up until (but excluding) index `i`
	newSlice = append(newSlice, s[:i]...) // [1, 2]

	// Copy the elements in `s` after index `i`
	// up until the end of slice `s` to `newSlice`
	return append(newSlice, s[i+1:]...), nil
}

func main() {
	s := []int{1, 2, 3, 4, 5, 6}
	// Remove the element at the 2nd index (3)
	// and assign the resulting slice to `ns`
	ns, err := removeElement(s, 2)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(s)  // [1,2,3,4,5,6]: original slice is preserved
	fmt.Println(ns) // [1,2,4,5,6]

	ns[0] = 100
	fmt.Println(s)  // [1,2,3,4,5,6]: s remains unchanged
	fmt.Println(ns) // [100,2,4,5,6]
}

As you can see, this solution preserves the original s slice after removeElement is called, and further modifications of ns after the fact doesn’t cause s to change as well.

Remove elements from a slice while iterating

If you want to remove multiple elements from a slice in Go while iterating over it, make sure that you’re iterating from the end of the slice to the start. If you do it the other way round, your program will panic with an index out of bounds error due to the fact that the slice length keeps decreasing each time an element is removed.

Here’s an example that removes all even integers from a slice:

package main

import (
	"fmt"
)

func main() {
	s := []int{1, 2, 3, 4, 5, 6}
	for i := len(s) - 1; i >= 0; i-- {
		// this checks whether the integer is even
		// before removing it from the slice
		if s[i]%2 == 0 {
			s = append(s[:i], s[i+1:]...)
		}

	}

	fmt.Println(s) // [1,3,5]
}

Conclusion

You should be able to use the techniques presented above to solve any problem that involves removing an element from a slice in Go. If you have any further contribution, please leave a comment below.

Thanks for reading, and happy coding!