包含片的方法

在Go中有没有类似slice.contains(object)方法的东西,而不需要在片中的每个元素中进行搜索?

545877 次浏览

如果片已排序,则在sort中实现了二进制搜索。

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

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

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

Mostafa已经指出,这样的方法写起来很简单,mkb给了你一个提示,使用排序包中的二分搜索。但是如果您要做很多这样的包含检查,您也可以考虑使用地图代替。

通过使用value, ok := yourmap[key]习语来检查特定的映射键是否存在是很简单的。因为你对这个值不感兴趣,你也可以创建一个map[string]struct{}作为例子。在这里使用空struct{}的好处是它不需要任何额外的空间,并且Go的内部映射类型针对这种值进行了优化。因此,map[string] struct{}在Go世界中是一个流行的集合选择。

与其使用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

你可以使用反映包来迭代一个具体类型为片的接口:

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

不确定这里是否需要泛型。你只需要为你想要的行为订立契约。如果您想让自己的对象在集合中表现自己,那么在其他语言中,通过重写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
}

如果使用映射来根据键查找项目是不可行的,你可以考虑goderive工具。goderived生成包含方法的特定于类型的实现,使您的代码既可读又高效。

例子;

type Foo struct {
Field1 string
Field2 int
}


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

生成派生econtainsfoo方法:

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

这个方法将为衍生的包含生成:

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

goderived还支持很多其他有用的辅助方法,可以在go中应用函数式编程风格。

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
}

排序包提供了构建块,如果你的片已经排序或你愿意排序。

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)),因此检查它可以显示字符串是否包含已排序的片。

go样式:

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

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

例如,您有一个包含单个单词值的切片(例如:“是”,“不是”,“可能”)。这些结果被追加到一个片中。如果您想检查此片是否包含任何“可能”结果,您可以使用

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

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

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

为不存在的项索引映射将返回false。所以你可以说m[X]而不是_, ok := m[X]

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

我使用反射包创建了以下Contains函数。 此函数可用于各种类型,如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 < / p >

如果你有一个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)
}

有几个可以提供帮助的软件包,但这个似乎很有前途:

https://github.com/wesovilabs/koazee

var numbers = []int{1, 5, 4, 3, 2, 7, 1, 8, 2, 3}
contains, _ := stream.Contains(7)
fmt.Printf("stream.Contains(7): %v\n", contains)

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

func Contains[T comparable](s []T, e T) bool {
for _, v := range s {
if v == e {
return true
}
}
return false
}
从Go 1.18开始,你可以使用slices包——特别是泛型Contains函数: https://pkg.go.dev/golang.org/x/exp/slices#Contains . < / p >
go get golang.org/x/exp/slices
import  "golang.org/x/exp/slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true

请注意,由于这是在标准库之外的一个实验包,所以它不绑定到Go 1兼容性承诺™,并且在正式添加到标准库之前可能会更改。