包含切片的方法

vos*_*ith 174 go slice

是否有类似slice.contains(object)Go中的方法而不必搜索切片中的每个元素?

Ado*_*lfo 272

从 Go 1.21 开始,您可以使用从前面提到的实验包升级的stdlibslices包。

\n
import  "slices"\n\nthings := []string{"foo", "bar", "baz"}\nslices.Contains(things, "foo") // true\n
Run Code Online (Sandbox Code Playgroud)\n
\n

原答案:

\n

从 Go 1.18 开始,您可以使用slices包 \xe2\x80\x93 特别是通用Contains函数:\n https://pkg.go.dev/golang.org/x/exp/slices#Contains

\n
go get golang.org/x/exp/slices\n
Run Code Online (Sandbox Code Playgroud)\n
import  "golang.org/x/exp/slices"\n
Run Code Online (Sandbox Code Playgroud)\n
things := []string{"foo", "bar", "baz"}\nslices.Contains(things, "foo") // true\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,由于这是一个实验包,位于 stdlib 之外,因此它不受 Go 1 兼容性 Promise\xe2\x84\xa2 的约束,并且在正式添加到 stdlib 之前可能会发生变化。

\n

  • 不幸的是,您无法在旧版本的 macOS 上使用 Go 1.18 (2认同)

tux*_*21b 205

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

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

  • 另请注意,您必须编写`struct {} {}`来获取空结构的值,以便在想要添加元素时将其传递给地图.试试吧,如果遇到任何问题,请随时提出.如果您更容易理解(除非您有大量数据),您也可以使用Mostafa的解决方案. (22认同)
  • `map[string] bool` 与 `map[string] struct{}` 相比如何。`map[string] struct{}` 看起来像是一个 hack,特别是初始化一个空结构 `struct {}{}` (3认同)
  • @IgorPetrov 同意,​​我很惊讶这样的基本功能还没有在运行时中。 (3认同)
  • 解决方案很简单,这是事实。但是,将这些基本功能添加到运行时需要什么呢?我没有在github上的Go repo中发现这样的问题。真是可悲又奇怪。 (2认同)
  • 可笑的是你必须自己添加这个。 (2认同)

Mos*_*afa 156

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

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

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

  • 实际上它并不简单,因为你必须为你使用的每种类型编写一个,并且因为没有重载,你必须以不同的方式命名每个函数,就像在C. append()可以一般工作,因为它有特殊的运行时支持.由于同样的原因,通用包含有用,但实际上通用解决方案只是语言中的泛型支持. (214认同)
  • 平凡== 7行代码,包括1个循环1分支if语句和1个比较?我想我在这里遗漏了一些东西...... (75认同)
  • 但为什么不在 go core 本身中添加这些呢? (46认同)
  • 如果 Go 在这方面像 C 一样痛苦,那它还有什么意义呢……如果 `contains` 如此微不足道,那么将它添加到标准库中应该是不言自明的。 (24认同)
  • @Eloff`interface {}` (13认同)
  • @Alex Lockwood 这实际上可以与接口一起使用吗? (2认同)
  • @OryBand它会,但不是`==`,你必须使用http://golang.org/pkg/reflect/#DeepEqual (2认同)
  • @tothemario这个答案表明“写”是微不足道的……您的回答(正确地)表明,“运行”并不一定是微不足道的。 (2认同)
  • 这可能会成为泛型的核心。 (2认同)

小智 57

在 Go 1.18+ 中,我们可以使用泛型。

func Contains[T comparable](s []T, e T) bool {
    for _, v := range s {
        if v == e {
            return true
        }
    }
    return false
}
Run Code Online (Sandbox Code Playgroud)

  • Go 是我最喜欢的语言,因为我喜欢从头开始创建其他语言提供的 OOTB 实用程序。 (129认同)
  • 我宁愿花精力编写业务逻辑而不是编写实用程序。 (12认同)
  • @AbhijitSarkar 我意识到你在开玩笑,我也同意这应该在 stdlib 中,但泛型刚刚被引入 Go。我更喜欢一种对其引入的功能非常谨慎且相对简单的语言。我希望随着时间的推移,这将被添加到 Golang 中。 (8认同)
  • 似乎在 golang 中,他们正在用语言实现的简单性来换取使用它的任何人的额外复杂性。我假设每个 golang 开发人员只是在某个地方实现了所有基本功能,然后在他们的项目之间复制并粘贴它。它基本上又是 Javascript,只不过由于进入门槛较高,代码质量更高 (6认同)

Hen*_*sen 16

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

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
}
Run Code Online (Sandbox Code Playgroud)

SearchString承诺 return the index to insert x if x is not present (it could be len(a)),因此检查它会显示字符串是否包含已排序的切片。

  • 是的,二分搜索和函数“contains”是“O(log(n))”,但由于排序,总体方法是“O(n*log(n))”。 (3认同)
  • 就时间而言,常规搜索为“O(n)”,而此解决方案使其为“O(n*log(n))”。 (2认同)
  • @plesiv 这是一个二分搜索,AFAICS。这难道不是 O(log n) 吗? (2认同)

mkb*_*mkb 14

如果切片进行排序,则在执行二进制搜索sort.


hol*_*lys 10

而不是使用slice,map可能是一个更好的解决方案.

简单的例子:

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

}
Run Code Online (Sandbox Code Playgroud)

http://play.golang.org/p/CEG6cu4JTf

  • 在当前形式中,此代码没有任何好处,因为如果您只打算使用一次,则从切片构造映射没有意义. - 为了有用,这段代码应该提供一个函数`sliceToMap`来完成所有的准备工作.在那之后,查询地图是微不足道和有效的. (29认同)

小智 8

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
}
Run Code Online (Sandbox Code Playgroud)


Bil*_*ick 7

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

为地图中不存在的项目建立索引将返回false。所以_, ok := m[X]你可以直接说m[X].

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

  • 这很好,但请记住 struct{} 的空间复杂度为 0,而 bool 的空间复杂度更高 (2认同)

小智 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
}
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/jL5UD7yCNq

  • 当然您可以使用 Reflect 包,但仅仅因为您可以,并不意味着您应该使用。反思是非常昂贵的。 (7认同)