Is there anything similar to a slice.contains(object) method in Go without having to do a search through each element in a slice?
-
8github.com/forestgiant/sliceutilRodrigo– Rodrigo2016-03-28 18:51:26 +00:00Commented Mar 28, 2016 at 18:51
-
3Try never to use a third-party package for such a work, like @Rodrigo you offered. It makes you code bulky and fragileIgor A. Melekhine– Igor A. Melekhine2022-05-03 07:41:49 +00:00Commented May 3, 2022 at 7:41
-
go.dev/play/p/B1qGeOLI9NaOmkesh Sajjanwar– Omkesh Sajjanwar2022-07-12 12:59:47 +00:00Commented Jul 12, 2022 at 12:59
-
1There is no possible way to search a slice without visiting every element, so it will always be O(N). If you need scalability, a slice is the wrong data structure.Jeff Learman– Jeff Learman2023-07-24 12:38:09 +00:00Commented Jul 24, 2023 at 12:38
-
5Since Go 1.21: slices.Containsguettli– guettli2024-05-02 06:13:03 +00:00Commented May 2, 2024 at 6:13
20 Answers
As of Go 1.21, you can use the stdlib slices package which was promoted from the experimental package previously mentioned.
import "slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true
Original Answer:
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.
2 Comments
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.
8 Comments
struct{}{} to get the value of the empty struct so that you can pass it to your map when you want to add an element. Just try it, and if you encounter any problems, feel free to ask. You can also use Mostafa's solution if that's easier for you to understand (unless you have huge amounts of data).map[string] bool compare with map[string] struct{}. map[string] struct{} seems like a hack especially initializing an empty struct struct {}{}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.
9 Comments
contains is so trivial, it should be self explanatory to add it to the standard library.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
}
6 Comments
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.
4 Comments
O(n) and this solution makes it O(n*log(n)).contains are O(log(n)), but the overall approach is O(n*log(n)) due to the sort.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))
}
2 Comments
sliceToMap that does all the preparation. After that, querying the map is trivial and efficient.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
}
Comments
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.
1 Comment
Complementing @Adolfo's answer (and other answers here), the Contains function won't be something experimental anymore. Starting with GO v1.21, which will be released during August 2023, the aforementioned slice package will be included into the core library (besides some other interesting packages like map and cmp), making it possible to run a linear search over a slice of elements (O(N) time complexity).
Additionally you will have some other interesting variations for searching an element (or elements) within a slice, like the new ContainsFunc, which reports whether at least one element e of s satisfies f(e). You can check this at the example below, which is taken from the actual docs:
package main
import (
"fmt"
"slices"
)
func main() {
numbers := []int{0, 42, -10, 8}
hasNegative := slices.ContainsFunc(numbers, func(n int) bool {
return n < 0
})
fmt.Println("Has a negative:", hasNegative)
hasOdd := slices.ContainsFunc(numbers, func(n int) bool {
return n%2 != 0
})
fmt.Println("Has an odd number:", hasOdd)
}
Also, to keep in mind, if you are working with a sorted slice and you want to reduce the time complexity when searching for an element (O(logN)), you will also be able to use the BinarySearch and the BinarySearchFunc functions, which will also come with this new package.
Finally, if you want to make the search constant in time (O(1)), I would go for the approach suggested by @tux21b in the voted answer by using maps.
Comments
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
}
2 Comments
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.
Comments
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
}
2 Comments
Contains() is implemented on List<T>, so you only ever have to implement Equals() for that work.this and that in IsIdentical makes no sense and is useless – you're just taking the memory addresses of the stack values this and that, not the addresses of the concrete objects the interfaces point to, and they'll always be different anyway. second, Go slice types are not covariant over the contained type, so you are not able to just pass a []T, where T implements Identifiable, to contains – you'd need to manually convert the slice.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"
})
1 Comment
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)
}
Comments
As others have stated almost all of these answers are wrong because they did not read the question correctly. The SAME function needs to return true if the Binary Trees have THE SAME MEMBER NODES, NOT THE SAME STRUCTURE. So you need to create two slices for the channels to feed into and then compare membership of those slices to see if both trees have the same node values. It doesn't matter if the nodes are in different places. Check the actual question here: https://go.dev/tour/concurrency/8
package main
import (
"golang.org/x/tour/tree"
"fmt"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int){
//fmt.Println("WALKING",t.Value)
ch <- t.Value
if( t.Left == nil && t.Right == nil ){
//fmt.Println("End of Branch")
}
if(t.Left != nil){
Walk(t.Left,ch)
}
if(t.Right != nil){
Walk(t.Right,ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool{
ch1 := make(chan int)
ch2 := make(chan int)
var k int = 10
go Walk(t1,ch1)
go Walk(t2,ch2)
tree1Slice := make([]int,k)
tree2Slice := make([]int,k)
for range k {
tree1Slice = append(tree1Slice,<-ch1)
tree2Slice = append(tree2Slice,<-ch2)
}
close(ch1)
close(ch2)
inArray := func(arr []int, value int) bool {
for a := range arr {
if arr[a] == value {
return true
}
}
return false
}
for i:=0;i<len(tree1Slice);i++ {
if ( !inArray(tree2Slice,tree1Slice[i]) ){
return false
}
}
return true
}
func main() {
tree1 := tree.New(1)
tree2 := tree.New(1)
fmt.Println(Same(tree1,tree2))
}
Comments
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
Comments
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.
2 Comments
exSlice := ["yes and no", "maybe", "maybe another"]","+strings.Join(exSlice,",")+",", and ",maybe,"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)