5

I want to know is there a generic way to write code to judge whether a slice contains an element, I find it will frequently useful since there is a lot of logic to fist judge whether specific elem is already in a slice and then decide what to do next. But there seemed not a built-in method for that(For God's sake, why?)

I try to use interface{} to do that like:

func sliceContains(slice []interface{}, elem interface{}) bool { for _, item := range slice { if item == elem { return true } } return false } 

I thought interface{} is sort of like Object of Java, but apparently, I was wrong. Should I write this every time meet with a new struct of slice? Isn't there a generic way to do this?

1

4 Answers 4

4

You can do it with reflect, but it will be MUCH SLOWER than a non-generic equivalent function:

func Contains(slice, elem interface{}) bool { sv := reflect.ValueOf(slice) // Check that slice is actually a slice/array. // you might want to return an error here if sv.Kind() != reflect.Slice && sv.Kind() != reflect.Array { return false } // iterate the slice for i := 0; i < sv.Len(); i++ { // compare elem to the current slice element if elem == sv.Index(i).Interface() { return true } } // nothing found return false } func main(){ si := []int {3, 4, 5, 10, 11} ss := []string {"hello", "world", "foo", "bar"} fmt.Println(Contains(si, 3)) fmt.Println(Contains(si, 100)) fmt.Println(Contains(ss, "hello")) fmt.Println(Contains(ss, "baz")) } 

How much slower? about x50-x60 slower: Benchmarking against a non generic function of the form:

func ContainsNonGeneic(slice []int, elem int) bool { for _, i := range slice { if i == elem { return true } } return false } 

I'm getting:

  • Generic: N=100000, running time: 73.023214ms 730.23214 ns/op
  • Non Generic: N=100000, running time: 1.315262ms 13.15262 ns/op
Sign up to request clarification or add additional context in comments.

Comments

3

You can make it using the reflect package like that:

func In(s, e interface{}) bool { slice, elem := reflect.ValueOf(s), reflect.ValueOf(e) for i := 0; i < slice.Len(); i++ { if reflect.DeepEqual(slice.Index(i).Interface(), elem.Interface()) { return true } } return false } 

Playground examples: http://play.golang.org/p/TQrmwIk6B4

Alternatively, you can:

  • define an interface and make your slices implement it
  • use maps instead of slices
  • just write a simple for loop

What way to choose depends on the problem you are solving.

Comments

0

I'm not sure what your specific context is, but you'll probably want to use a map to check if something already exists.

package main import "fmt" type PublicClassObjectBuilderFactoryStructure struct { Tee string Hee string } func main() { // Empty structs occupy zero bytes. mymap := map[interface{}]struct{}{} one := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "hey"} two := PublicClassObjectBuilderFactoryStructure{Tee: "hola", Hee: "oye"} three := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "again"} mymap[one] = struct{}{} mymap[two] = struct{}{} // The underscore is ignoring the value, which is an empty struct. if _, exists := mymap[one]; exists { fmt.Println("one exists") } if _, exists := mymap[two]; exists { fmt.Println("two exists") } if _, exists := mymap[three]; exists { fmt.Println("three exists") } } 

Another advantage of using maps instead of a slice is that there is a built-in delete function for maps. https://play.golang.org/p/dmSyyryyS8

Comments

0

If you want a rather different solution, you might try the code-generator approach offered by tools such as Gen. Gen writes source code for each concrete class you want to hold in a slice, so it supports type-safe slices that let you search for the first match of an element.

(Gen also offers a few other kinds of collection and allows you to write your own.)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.