包含切片的方法


Answers:


226

Mostafa已经指出,编写这种方法很简单,而mkb为您提供了使用sort包中的二进制搜索的提示。但是,如果要进行很多此类包含检查,则还可以考虑使用地图。

使用value, ok := yourmap[key]惯用语检查特定的映射键是否存在很简单。由于您对值不感兴趣,因此也可以创建一个map[string]struct{}例如。struct{}在此处使用空值的优点是不需要任何额外的空间,并且Go的内部映射类型针对该类型的值进行了优化。因此,map[string] struct{}在围棋世界中是流行的选择。


27
另请注意,您必须编写操作struct{}{}以获取空结构的值,以便在想要添加元素时可以将其传递给地图。只需尝试一下,如果遇到任何问题,请随时提问。如果您更容易理解,也可以使用Mostafa的解决方案(除非您有大量数据)。
tux21b 2012年

5
解决方案很简单,这是事实。但是,将这些基本功能添加到运行时需要什么呢?我没有在github上的Go repo中发现这样的问题。真是可悲又奇怪。
伊戈尔·彼得罗夫

1
map[string] bool相比map[string] struct{}map[string] struct{}似乎是黑客,尤其是初始化一个空结构struct {}{}
vadasambar

@IgorPetrov同意,我很惊讶这样的基本功能尚未在运行时中。
jcollum

179

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

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

如果该查找是代码的重要组成部分,则可以使用地图,但是地图也有成本。


257
实际上,这并不是一件容易的事,因为您必须为所使用的每种类型编写一个,并且由于没有重载,因此您必须对每个函数进行不同的命名,例如在C中。append()可以通用地工作,因为它具有特殊的运行时支持。出于相同的原因,泛型包含将很有用,但实际上,泛型解决方案只是该语言中的泛型支持。
Eloff 2014年

15
@Eloffinterface{}
Alex Lockwood

2
@Alex Lockwood可以在接口上实际使用吗?
奥里乐队

100
平凡== 7行代码,包括1个循环1个if语句分支和1个比较?我想我在这里想念的东西……
tothemario '16

3
但是为什么不将这些添加到go core本身呢?
露娜·洛夫古德


11

而不是使用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


34
在当前形式下,此代码无济于事,因为如果您只想使用一个切片,则从切片构造地图是没有意义的。—为了有用,此代码应该提供sliceToMap完成所有准备工作的功能。之后,查询地图是简单而有效的。
罗兰·伊利格

9

排序包提供了构建块,如果你的片进行排序,或者您愿意排序。

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)),因此检查该字符串会发现字符串是否包含已排序的片。


就时间而言,常规搜索为,O(n)并且此解决方案可以做到O(n*log(n))
plesiv

@plesiv,它是一个二进制搜索,AFAICS。那不是使它成为O(log n)吗?
Henrik AastedSørensen

是的,二进制搜索和函数containsO(log(n)),但是整体方法O(n*log(n))归因于排序。
plesiv

3

您可以使用反射包在具体类型为切片的接口上进行迭代:

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


3
当然,您可以使用反射包,但是仅仅因为可以,并不意味着您应该这样做。反射非常昂贵。
贾斯汀·奥姆斯

3

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

例;

type Foo struct {
    Field1 string
    Field2 int
} 

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

要生成deriveContainsFoo方法:

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

此方法将为generateContains生成:

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

Goderive支持许多其他有用的辅助方法,以在go中应用函数式编程样式。


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
}

2

不确定此处是否需要泛型。您只需要为所需的行为订立合同即可。如果您希望自己的对象在集合中表现自己,例如通过覆盖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
}

1
“仅是用其他语言要做的事情”并不是真的-例如,在C#Contains()中实现List<T>,因此您只需实现Equals()该工作即可。
乔治


0

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

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

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

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


对于元素具有相似文本但不完全相同的元素将不起作用exSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal,

这是一种实现快速简单的单线解决方案的不错的方法。您只需要一个明确的定界符(可以是逗号),并做额外的工作即可将两个字符串都","+strings.Join(exSlice,",")+","",maybe,"
括起来

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

2
这不会回答问题,也不会提供其他信息。
Croolman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.