Create a new slice given a previous one, without a given value

Viewed 48

I have a slice of strings. What I need to accomplish is to remove one value from the slice, without knowing the index. I thought this would be the easiest way to do it:


// Return a slice that is the original without the given string
func newSliceWithout(s []string, without string) []string {
    l := []string{}
    for _, elem := range s {
        if elem != without {
            l = append(l, elem)
        }
    }
    return l
}

However, when performing benchmarks I get pretty high values (as expected), so I'm wondering if there's a faster / more efficient way to do it?

1 Answers

Allocate a big return slice in one step (estimated by the input slice), and don't use append() but assign to individual elements:

func newSliceWithout(s []string, without string) []string {
    l, i := make([]string, len(s)), 0
    for _, elem := range s {
        if elem != without {
            l[i] = elem
            i++
        }
    }
    return l[:i]
}

Testing it:

x := newSliceWithout([]string{"a", "b", "a", "c"}, "a")
fmt.Println(x)

which outputs (try it on the Go Playground):

[b c]

Benchmarks

func BenchmarkNewSliceWithoutOrig(b *testing.B) {
    for n := 0; n < b.N; n++ {
        newSliceWithoutOrig([]string{"a", "b", "a", "c"}, "a")
    }
}

func BenchmarkNewSliceWithout(b *testing.B) {
    for n := 0; n < b.N; n++ {
        newSliceWithout([]string{"a", "b", "a", "c"}, "a")

    }
}

Result:

BenchmarkNewSliceWithoutOrig-8    7416729   139.0 ns/op   48 B/op  2 allocs/op
BenchmarkNewSliceWithout-8       13280971   103.4 ns/op   64 B/op  1 allocs/op

If we use a bigger slice:

var s = []string{"a", "b", "x", "y", "z", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "c"}

Then benchmark results:

BenchmarkNewSliceWithoutOrig-8   2253637   566.8 ns/op   496 B/op    5 allocs/op
BenchmarkNewSliceWithout-8       5160316   224.3 ns/op   256 B/op    1 allocs/op
Related