包含切片的方法

时间:2012-05-07 16:35:38

标签: go slice

Go中是否有类似于slice.contains(object)方法的内容,而无需搜索切片中的每个元素?

17 个答案:

答案 0 :(得分:184)

Mostafa已经指出这样的方法写得很简单,而且mkb给你一个提示,可以使用sort包中的二进制搜索。但是如果你打算做很多这样的包含检查,你也可以考虑使用地图。

使用value, ok := yourmap[key]惯用法来检查特定的地图密钥是否存在是微不足道的。由于您对该值不感兴趣,因此您也可以创建一个map[string]struct{}。在这里使用空struct{}的优点是它不需要任何额外的空间,并且Go的内部地图类型针对这种值进行了优化。因此,map[string] struct{}是Go世界中广告集的热门选择。

答案 1 :(得分:128)

不,这种方法不存在,但写起来很简单:

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

如果该查找是代码的重要部分,则可以使用地图,但地图也需要花费。

答案 2 :(得分:12)

如果切片已排序,则在the sort package中实施二进制搜索。

答案 3 :(得分:7)

而不是使用slicemap可能是更好的解决方案。

简单的例子:

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

答案 4 :(得分:6)

如果对切片进行了排序或您愿意对其进行排序,则sort软件包将提供构建块。

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承诺会返回the index to insert x if x is not present (it could be len(a)),因此检查该字符串会发现字符串是否包含排序后的切片。

答案 5 :(得分:5)

您可以使用 reflect 包来迭代具体类型为切片的接口:

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

答案 6 :(得分:3)

不确定这里是否需要泛型。您只需要一份合同即可达到预期的行为。如果您希望自己的对象在集合中表现自己,通过覆盖Equals()和GetHashCode(),执行以下操作只不过是您在其他语言中必须执行的操作。

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
}

答案 7 :(得分:3)

当前Contains包中有slice函数。您可以阅读文档here

样品用量:

if !slice.Contains(sliceVar, valueToFind) {
    //code here
}

答案 8 :(得分:2)

如果使用地图基于某个键查找项目是不可行的,您可以考虑使用goderive工具。 Goderive生成包含方法的类型特定实现,使您的代码既可读又高效。

实施例

type Foo struct {
    Field1 string
    Field2 int
} 

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

生成deriveContainsFoo方法:

  • 使用go get -u github.com/awalterschulze/goderive
  • 安装goderive
  • 在工作区文件夹中运行goderive ./...

将为deriveContains:

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

Goderive支持其他一些有用的辅助方法来在go中应用函数式编程风格。

答案 9 :(得分:2)

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
}

答案 10 :(得分:1)

我用这些答案中的解决方案创建了一个非常简单的基准。

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

这不是一个真正的基准,因为起初我没有插入太多元素,但是可以随意进行分叉和更改。

答案 11 :(得分:0)

在其他主题中,我以两种方式评论了此问题的解决方案:

第一种方法:

action .student

使用示例:

func Find(slice interface{}, f func(value interface{}) bool) int {
    s := reflect.ValueOf(slice)
    if s.Kind() == reflect.Slice {
        for index := 0; index < s.Len(); index++ {
            if f(s.Index(index).Interface()) {
                return index
            }
        }
    }
    return -1
}

计算成本较低的第二种方法:

type UserInfo struct {
    UserId          int
}

func main() {
    var (
        destinationList []UserInfo
        userId      int = 123
    )
    
    destinationList = append(destinationList, UserInfo { 
        UserId          : 23,
    }) 
    destinationList = append(destinationList, UserInfo { 
        UserId          : 12,
    }) 
    
    idx := Find(destinationList, func(value interface{}) bool {
        return value.(UserInfo).UserId == userId
    })
    
    if idx < 0 {
        fmt.Println("not found")
    } else {
        fmt.Println(idx)    
    }
}

使用示例:

func Search(length int, f func(index int) bool) int {
    for index := 0; index < length; index++ {
        if f(index) {
            return index
        }
    }
    return -1
}

答案 12 :(得分:0)

如果您有 byte 切片,则可以使用 bytes 包:

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

suffixarray 包:

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

如果您有 int 切片,则可以使用 intsets 包:

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

答案 13 :(得分:-1)

围棋风格:

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"
})

答案 14 :(得分:-1)

它可能被认为有点“ hacky”,但是根据切片的大小和内容,您可以将切片连接在一起并进行字符串搜索。

例如,您有一个包含单个单词值的切片(例如,“是”,“否”,“可能”)。这些结果将附加到切片中。如果要检查此切片是否包含任何“也许”结果,则可以使用

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

这是否真正合适取决于片的大小及其成员的长度。大切片或长值可能存在性能或适用性问题,但对于有限大小和简单值的较小切片,它是实现所需结果的有效方法。

答案 15 :(得分:-1)

我认为map [x] bool比map [x] struct {}更有用。

为不存在的项目建立地图索引将返回false,因此可以代替_,好:= m [X],您可以说m [X]。

这使得在表达式中嵌套包含测试变得容易。

答案 16 :(得分:-1)

我使用反射包创建了以下包含函数。 此函数可用于各种类型,如 int32 或 struct 等。

// 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
}

contains 函数的用法如下

// 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

请参阅此处了解更多详情: https://github.com/glassonion1/xgo/blob/main/contains.go

相关问题