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

穿精又带淫゛_ 提交于 2019-12-20 02:16:21

问题


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:


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



回答2:


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.




回答3:


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




回答4:


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



来源:https://stackoverflow.com/questions/28828440/is-there-a-way-to-write-generic-code-to-find-out-whether-a-slice-contains-specif

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!