Is there a way to write generic code to find out whether a slice contains specific element in Go?

后端 未结 4 614
离开以前
离开以前 2021-01-20 13:38

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 judg

相关标签:
4条回答
  • 2021-01-20 14:05

    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
    0 讨论(0)
  • 2021-01-20 14:09

    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.

    0 讨论(0)
  • 2021-01-20 14:09

    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

    0 讨论(0)
  • 2021-01-20 14:14

    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.)

    0 讨论(0)
提交回复
热议问题