Remove element by value in Go list
Asked Answered
S

3

18

I have a list of elements and I want to remove one of them, by value. In Python this would be

l = ["apples", "oranges", "melon"]
l.remove("melon")
print(l) # ["apples", "orange"]

What is the equivalent in Go? I found a slice trick to remove an element by index, but it's not very readable, still requires me to find the index manually and only works for a single item type:

func remove(l []string, item string) {
    for i, other := range l {
        if other == item {
            return append(l[:i], l[i+1:]...)
        }
    }
}

There's the list.List structure, but it's not generic and thus requires tons of type-castings to use.

What is the idiomatic way of removing an element from a list?

Shoal answered 26/6, 2015 at 18:55 Comment(2)
let me clarify, you need to remove an item from slice or the container list ?Garey
I currently have a slice, but I'm willing to replace it with a container list or anything else that does the job.Shoal
I
23

The idiomatic way to remove an element from a list is to loop through it exactly like you do in your example. Removing an element by value from a slice shouldn't be too common in your program since it is an O(n) operation and there are better data structures in the language for that. Therefore, Go does not provide a built-in remove function for slices.

If you find yourself using removal by value often, consider using a set instead where removing and adding an element is O(1) while still being iterable.

set := map[string]bool{"apples":true, "oranges":true, "melon":true}
delete(set,"melon") // is O(1)
Idelson answered 26/6, 2015 at 19:10 Comment(1)
I would argue that using a set-based approach is not always suitable, because set is generally unordered. So when the task is: "remove element by value from a sorted data collection", set won't help.Balkh
T
9

In generic Go (1.18) the filter function works on any comparable type. It removes only the first occurrence of the item.

func remove[T comparable](l []T, item T) []T {
    for i, other := range l {
        if other == item {
            return append(l[:i], l[i+1:]...)
        }
    }
    return l
}

Playground: https://go.dev/play/p/ojlYkvf5dQG?v=gotip


If you want to remove all occurrences, append the items that don't match to a new slice:

func remove[T comparable](l []T, item T) []T {
    out := make([]T, 0)
    for _, element := range l {
        if element != item {
            out = append(out, element)
        }
    }
    return out
}

Playground: https://go.dev/play/p/W2MerNbh72H


If the slice items aren't comparable, you can use a custom equality function to filter:

func main() {
    list := [][]int{{1, 2}, {2, 2}, {1, 10}}
    newlist := remove(list, func(element []int) bool { return element[0] == 1 })
    fmt.Println(newlist)
}

func remove[T any](l []T, remove func(T) bool) []T {
    out := make([]T, 0)
    for _, element := range l {
        if !remove(element) {
            out = append(out, element)
        }
    }
    return out
}

Playground: https://go.dev/play/p/qZWoFbM_RUl

Trimester answered 21/1, 2022 at 22:59 Comment(0)
T
0

Maybe not idiomic, but there is slices.DeleteFunc:

import "slices"

l := []string{"apples", "oranges", "melon"}

l = slices.DeleteFunc(l, func(cmp string) bool {
  return cmp == "melon"
})
Tse answered 1/7 at 17:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.