Contains method for a slice

GoSlice

Go Problem Overview


Is there anything similar to a slice.contains(object) method in Go without having to do a search through each element in a slice?

Go Solutions


Solution 1 - Go

Mostafa has already pointed out that such a method is trivial to write, and mkb gave you a hint to use the binary search from the sort package. But if you are going to do a lot of such contains checks, you might also consider using a map instead.

It's trivial to check if a specific map key exists by using the value, ok := yourmap[key] idiom. Since you aren't interested in the value, you might also create a map[string]struct{} for example. Using an empty struct{} here has the advantage that it doesn't require any additional space and Go's internal map type is optimized for that kind of values. Therefore, map[string] struct{} is a popular choice for sets in the Go world.

Solution 2 - Go

No, such method does not exist, but is trivial to write:

func contains(s []int, e int) bool {
    for _, a := range s {
	    if a == e {
		    return true
	    }
    }
    return false
}

You can use a map if that lookup is an important part of your code, but maps have cost too.

Solution 3 - Go

The sort package provides the building blocks if your slice is sorted or you are willing to sort it.

input := []string{"bird", "apple", "ocean", "fork", "anchor"}
sort.Strings(input)

fmt.Println(contains(input, "apple")) // true
fmt.Println(contains(input, "grow"))  // false

...

func contains(s []string, searchterm string) bool {
	i := sort.SearchStrings(s, searchterm)
	return i < len(s) && s[i] == searchterm
}

SearchString promises to return the index to insert x if x is not present (it could be len(a)), so a check of that reveals whether the string is contained the sorted slice.

Solution 4 - Go

Starting with Go 1.18, you can use the slices package – specifically the generic Contains function: https://pkg.go.dev/golang.org/x/exp/slices#Contains.

go get golang.org/x/exp/slices
import 	"golang.org/x/exp/slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true

Note that since this is outside the stdlib as an experimental package, it is not bound to the Go 1 Compatibility Promise™ and may change before being formally added to the stdlib.

Solution 5 - Go

Instead of using a slice, map may be a better solution.

simple example:

package main

import "fmt"


func contains(slice []string, item string) bool {
	set := make(map[string]struct{}, len(slice))
	for _, s := range slice {
		set[s] = struct{}{}
	}
	
	_, ok := set[item] 
	return ok
}

func main() {

	s := []string{"a", "b"}
	s1 := "a"
	fmt.Println(contains(s, s1))
	
}

http://play.golang.org/p/CEG6cu4JTf

Solution 6 - Go

If the slice is sorted, there is a binary search implemented in the sort package.

Solution 7 - Go

With Go 1.18+ we could use generics.

func Contains[T comparable](s []T, e T) bool {
	for _, v := range s {
		if v == e {
			return true
		}
	}
	return false
}

Solution 8 - Go

func Contain(target interface{}, list interface{}) (bool, int) {
	if reflect.TypeOf(list).Kind() == reflect.Slice || reflect.TypeOf(list).Kind() == reflect.Array {
		listvalue := reflect.ValueOf(list)
		for i := 0; i < listvalue.Len(); i++ {
			if target == listvalue.Index(i).Interface() {
				return true, i
			}
		}
	}
	if reflect.TypeOf(target).Kind() == reflect.String && reflect.TypeOf(list).Kind() == reflect.String {
		return strings.Contains(list.(string), target.(string)), strings.Index(list.(string), target.(string))
	}
	return false, -1
}

Solution 9 - Go

You can use the reflect package to iterate over an interface whose concrete type is a slice:

func HasElem(s interface{}, elem interface{}) bool {
    arrV := reflect.ValueOf(s)

	if arrV.Kind() == reflect.Slice {
    	for i := 0; i < arrV.Len(); i++ {
	
	    	// XXX - panics if slice element points to an unexported struct field
		    // see https://golang.org/pkg/reflect/#Value.Interface
			if arrV.Index(i).Interface() == elem {
    			return true
	    	}
	    }
	}

    return false
}

https://play.golang.org/p/jL5UD7yCNq

Solution 10 - Go

Not sure generics are needed here. You just need a contract for your desired behavior. Doing the following is no more than what you would have to do in other languages if you wanted your own objects to behave themselves in collections, by overriding Equals() and GetHashCode() for instance.

type Identifiable interface{
	GetIdentity() string
}

func IsIdentical(this Identifiable, that Identifiable) bool{
	return (&this == &that) || (this.GetIdentity() == that.GetIdentity())
}

func contains(s []Identifiable, e Identifiable) bool {
	for _, a := range s {
		if IsIdentical(a,e) {
			return true
		}
	}
	return false
}

Solution 11 - Go

If it is not feasable to use a map for finding items based on a key, you can consider the goderive tool. Goderive generates a type specific implementation of a contains method, making your code both readable and efficient.

Example;

type Foo struct {
    Field1 string
    Field2 int
} 

func Test(m Foo) bool {
     var allItems []Foo
     return deriveContainsFoo(allItems, m)
}

To generate the deriveContainsFoo method:

  • Install goderive with go get -u github.com/awalterschulze/goderive
  • Run goderive ./... in your workspace folder

This method will be generated for deriveContains:

func deriveContainsFoo(list []Foo, item Foo) bool {
	for _, v := range list {
		if v == item {
			return true
		}
	}
	return false
}

Goderive has support for quite some other useful helper methods to apply a functional programming style in go.

Solution 12 - Go

I created a very simple benchmark with the solutions from these answers.

https://gist.github.com/NorbertFenk/7bed6760198800207e84f141c41d93c7

It isn't a real benchmark because initially, I haven't inserted too many elements but feel free to fork and change it.

Solution 13 - Go

I think map[x]bool is more useful than map[x]struct{}.

Indexing the map for an item that isn't present will return false. so instead of _, ok := m[X], you can just say m[X].

This makes it easy to nest inclusion tests in expressions.

Solution 14 - Go

The go style:

func Contains(n int, match func(i int) bool) bool {
	for i := 0; i < n; i++ {
		if match(i) {
			return true
		}
	}
	return false
}


s := []string{"a", "b", "c", "o"}
// test if s contains "o"
ok := Contains(len(s), func(i int) bool {
	return s[i] == "o"
})

Solution 15 - Go

If you have a byte slice, you can use bytes package:

package main
import "bytes"

func contains(b []byte, sub byte) bool {
   return bytes.Contains(b, []byte{sub})
}

func main() {
   b := contains([]byte{10, 11, 12, 13, 14}, 13)
   println(b)
}

Or suffixarray package:

package main
import "index/suffixarray"

func contains(b []byte, sub byte) bool {
   return suffixarray.New(b).Lookup([]byte{sub}, 1) != nil
}

func main() {
   b := contains([]byte{10, 11, 12, 13, 14}, 13)
   println(b)
}

If you have an int slice, you can use intsets package:

package main
import "golang.org/x/tools/container/intsets"

func main() {
   var s intsets.Sparse
   for n := 10; n < 20; n++ {
      s.Insert(n)
   }
   b := s.Has(16)
   println(b)
}

Solution 16 - Go

There are several packages that can help, but this one seems promising:

https://github.com/wesovilabs/koazee

var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}
contains, _ := stream.Contains(7)
fmt.Printf("stream.Contains(7): %v\n", contains)

Solution 17 - Go

I created the following Contains function using reflect package. This function can be used for various types like int32 or struct etc.

// Contains returns true if an element is present in a slice
func Contains(list interface{}, elem interface{}) bool {
	listV := reflect.ValueOf(list)

	if listV.Kind() == reflect.Slice {
		for i := 0; i < listV.Len(); i++ {
			item := listV.Index(i).Interface()

			target := reflect.ValueOf(elem).Convert(reflect.TypeOf(item)).Interface()
			if ok := reflect.DeepEqual(item, target); ok {
				return true
			}
		}
	}
	return false
}

Usage of contains function is below

// slice of int32
containsInt32 := Contains([]int32{1, 2, 3, 4, 5}, 3)
fmt.Println("contains int32:", containsInt32)

// slice of float64
containsFloat64 := Contains([]float64{1.1, 2.2, 3.3, 4.4, 5.5}, 4.4)
fmt.Println("contains float64:", containsFloat64)


// slice of struct
type item struct {
    ID   string
    Name string
}
list := []item{
    item{
        ID:   "1",
        Name: "test1",
    },
    item{
        ID:   "2",
        Name: "test2",
    },
    item{
        ID:   "3",
        Name: "test3",
    },
}
target := item{
	ID:   "2",
	Name: "test2",
}
containsStruct := Contains(list, target)
fmt.Println("contains struct:", containsStruct)

// Output:
// contains int32: true
// contains float64: true
// contains struct: true

Please see here for more details: https://github.com/glassonion1/xgo/blob/main/contains.go

Solution 18 - Go

It might be considered a bit 'hacky' but depending the size and contents of the slice, you can join the slice together and do a string search.

For example you have a slice containing single word values (e.g. "yes", "no", "maybe"). These results are appended to a slice. If you want to check if this slice contains any "maybe" results, you may use

exSlice := ["yes", "no", "yes", "maybe"]
if strings.Contains(strings.Join(exSlice, ","), "maybe") {
  fmt.Println("We have a maybe!")
}

How suitable this is really depends on the size of the slice and length of its members. There may be performance or suitability issues for large slices or long values, but for smaller slices of finite size and simple values it is a valid one-liner to achieve the desired result.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionvosmithView Question on Stackoverflow
Solution 1 - Gotux21bView Answer on Stackoverflow
Solution 2 - GoMostafaView Answer on Stackoverflow
Solution 3 - GoHenrik Aasted SørensenView Answer on Stackoverflow
Solution 4 - GoAdolfoView Answer on Stackoverflow
Solution 5 - GoholysView Answer on Stackoverflow
Solution 6 - GoMatt KView Answer on Stackoverflow
Solution 7 - GoAmarView Answer on Stackoverflow
Solution 8 - GoJim HsiangView Answer on Stackoverflow
Solution 9 - GoEthan KennedyView Answer on Stackoverflow
Solution 10 - GoJonPenView Answer on Stackoverflow
Solution 11 - GoAlexander van TrijffelView Answer on Stackoverflow
Solution 12 - GoF. NorbertView Answer on Stackoverflow
Solution 13 - GoBill BurdickView Answer on Stackoverflow
Solution 14 - GoRoy O'YoungView Answer on Stackoverflow
Solution 15 - GoZomboView Answer on Stackoverflow
Solution 16 - GonatenhoView Answer on Stackoverflow
Solution 17 - Goglassonion1View Answer on Stackoverflow
Solution 18 - Gochopper24View Answer on Stackoverflow