包含切片的方法

huangapple go评论82阅读模式
英文:

Contains method for a slice

问题

在Go语言中是否有类似于slice.contains(object)的方法,而不需要在切片中每个元素进行搜索?

英文:

Is there anything similar to a slice.contains(object) method in Go without having to do a search through each element in a slice?

答案1

得分: 364

Mostafa已经指出,编写这样一个方法是微不足道的,并且mkb给了你一个提示,使用sort包中的二分查找。但是,如果你要做很多这样的包含检查,你也可以考虑使用map。

使用value, ok := yourmap[key]的习惯用法可以轻松检查特定的map键是否存在。由于你对值不感兴趣,你也可以创建一个map[string]struct{}。在这里使用空的struct{}的优点是它不需要任何额外的空间,而且Go的内部map类型对这种类型的值进行了优化。因此,在Go世界中,map[string] struct{}是一种流行的集合选择。

英文:

Mostafa has already pointed out that such a method is trivial to write, and mkb gave you a hint to use the binary search from the sort package. But if you are going to do a lot of such contains checks, you might also consider using a map instead.

It's trivial to check if a specific map key exists by using the value, ok := yourmap[key] idiom. Since you aren't interested in the value, you might also create a map[string]struct{} for example. Using an empty struct{} here has the advantage that it doesn't require any additional space and Go's internal map type is optimized for that kind of values. Therefore, map[string] struct{} is a popular choice for sets in the Go world.

答案2

得分: 336

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

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

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

英文:

No, such method does not exist, but is trivial to write:

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

You can use a map if that lookup is an important part of your code, but maps have cost too.

答案3

得分: 207

从Go 1.21开始,您可以使用stdlib的slices包,该包已从先前提到的实验性包中提升。

import "slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true

原始答案:

从Go 1.18开始,您可以使用slices包,特别是泛型的Contains函数:
https://pkg.go.dev/golang.org/x/exp/slices#Contains

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 Compatibility Promise™的一部分,因此在正式添加到stdlib之前可能会发生更改。

英文:

As of Go 1.21, you can use the stdlib slices package which was promoted from the experimental package previously mentioned.

import 	"slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true

Original Answer:

Starting with Go 1.18, you can use the slices package – specifically the generic Contains function:
https://pkg.go.dev/golang.org/x/exp/slices#Contains.

go get golang.org/x/exp/slices
import 	"golang.org/x/exp/slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true

Note that since this is outside the stdlib as an experimental package, it is not bound to the Go 1 Compatibility Promise™ and may change before being formally added to the stdlib.

答案4

得分: 53

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

func Contains[T comparable](s []T, e T) bool {
	for _, v := range s {
		if v == e {
			return true
		}
	}
	return false
}
英文:

With Go 1.18+ we could use generics.

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

答案5

得分: 26

The sort package provides the building blocks if your slice is sorted or you are willing to sort it.

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 promises to return the index to insert x if x is not present (it could be len(a)), so a check of that reveals whether the string is contained the sorted slice.

英文:

The sort package provides the building blocks if your slice is sorted or you are willing to sort it.

input := []string{&quot;bird&quot;, &quot;apple&quot;, &quot;ocean&quot;, &quot;fork&quot;, &quot;anchor&quot;}
sort.Strings(input)

fmt.Println(contains(input, &quot;apple&quot;)) // true
fmt.Println(contains(input, &quot;grow&quot;))  // false

...

func contains(s []string, searchterm string) bool {
	i := sort.SearchStrings(s, searchterm)
	return i &lt; len(s) &amp;&amp; s[i] == searchterm
}

SearchString promises to return the index to insert x if x is not present (it could be len(a)), so a check of that reveals whether the string is contained the sorted slice.

答案6

得分: 20

代替使用slicemap可能是一个更好的解决方案。

简单示例:

package main

import "fmt"


func contains(slice []string, item string) bool {
    set := make(map[string]struct{}, len(slice))
    for _, s := range slice {
        set
展开收缩
= 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

英文:

Instead of using a slice, map may be a better solution.

simple example:

package main

import &quot;fmt&quot;


func contains(slice []string, item string) bool {
	set := make(map[string]struct{}, len(slice))
	for _, s := range slice {
		set
展开收缩
= struct{}{} } _, ok := set[item] return ok } func main() { s := []string{&quot;a&quot;, &quot;b&quot;} s1 := &quot;a&quot; fmt.Println(contains(s, s1)) }

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

答案7

得分: 18

如果切片已经排序,可以在sort包中实现二分查找。

英文:

If the slice is sorted, there is a binary search implemented in the sort package.

答案8

得分: 9

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
}

英文:
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 &lt; listvalue.Len(); i++ {
			if target == listvalue.Index(i).Interface() {
				return true, i
			}
		}
	}
	if reflect.TypeOf(target).Kind() == reflect.String &amp;&amp; reflect.TypeOf(list).Kind() == reflect.String {
		return strings.Contains(list.(string), target.(string)), strings.Index(list.(string), target.(string))
	}
	return false, -1
}

答案9

得分: 6

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

如果索引的项不存在,映射将返回false。所以,你可以直接使用m[X],而不是_, ok := m[X]

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

英文:

I think map[x]bool is more useful than map[x]struct{}.

Indexing the map for an item that isn't present will return false. so instead of _, ok := m[X], you can just say m[X].

This makes it easy to nest inclusion tests in expressions.

答案10

得分: 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 - 如果切片元素指向一个未导出的结构体字段,则会引发恐慌
            // 参见 https://golang.org/pkg/reflect/#Value.Interface
            if arrV.Index(i).Interface() == elem {
                return true
            }
        }
    }

    return false
}

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

英文:

You can use the reflect package to iterate over an interface whose concrete type is a slice:

func HasElem(s interface{}, elem interface{}) bool {
    arrV := reflect.ValueOf(s)

	if arrV.Kind() == reflect.Slice {
    	for i := 0; i &lt; 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

答案11

得分: 4

这里可能不需要泛型。你只需要一个用于所需行为的合约。如果你想让自己的对象在集合中表现正常,你只需要像在其他语言中一样重写Equals()和GetHashCode()方法。

type Identifiable interface{
	GetIdentity() string
}

func IsIdentical(this Identifiable, that Identifiable) bool{
	return (&amp;this == &amp;that) || (this.GetIdentity() == that.GetIdentity())
}

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

Not sure generics are needed here. You just need a contract for your desired behavior. Doing the following is no more than what you would have to do in other languages if you wanted your own objects to behave themselves in collections, by overriding Equals() and GetHashCode() for instance.

type Identifiable interface{
	GetIdentity() string
}

func IsIdentical(this Identifiable, that Identifiable) bool{
	return (&amp;this == &amp;that) || (this.GetIdentity() == that.GetIdentity())
}

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

答案12

得分: 3

如果使用地图来根据键查找项目不可行,您可以考虑使用goderive工具。Goderive会生成一个特定类型的contains方法的实现,使您的代码既可读又高效。

示例:

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中应用函数式编程风格。

英文:

If it is not feasable to use a map for finding items based on a key, you can consider the goderive tool. Goderive generates a type specific implementation of a contains method, making your code both readable and efficient.

Example;

type Foo struct {
    Field1 string
    Field2 int
} 

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

To generate the deriveContainsFoo method:

  • Install goderive with go get -u github.com/awalterschulze/goderive
  • Run goderive ./... in your workspace folder

This method will be generated for deriveContains:

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

Goderive has support for quite some other useful helper methods to apply a functional programming style in go.

答案13

得分: 1

The go style:

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"
})
英文:

The go style:

func Contains(n int, match func(i int) bool) bool {
	for i := 0; i &lt; n; i++ {
		if match(i) {
			return true
		}
	}
	return false
}


s := []string{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;o&quot;}
// test if s contains &quot;o&quot;
ok := Contains(len(s), func(i int) bool {
	return s[i] == &quot;o&quot;
})

答案14

得分: 1

补充**@Adolfo的答案(以及其他答案),Contains函数将不再是实验性的。从GO v1.21**开始,该函数将被包含在核心库中(除了一些其他有趣的包,如mapcmp),从而使得在一个元素切片上进行线性搜索成为可能(O(N)时间复杂度)。

此外,您还可以使用其他一些有趣的变体来搜索切片中的元素(或元素),例如新的ContainsFunc,它报告切片s中是否至少有一个元素satisfies f(e)。您可以在下面的示例中查看这一点,该示例摘自实际文档:

package main

import (
	"fmt"
	"slices"
)

func main() {
	numbers := []int{0, 42, -10, 8}
	hasNegative := slices.ContainsFunc(numbers, func(n int) bool {
		return n < 0
	})
	fmt.Println("Has a negative:", hasNegative)
	hasOdd := slices.ContainsFunc(numbers, func(n int) bool {
		return n%2 != 0
	})
	fmt.Println("Has an odd number:", hasOdd)
}

另外,需要记住的是,如果您使用的是排序切片,并且希望在搜索元素时降低时间复杂度(O(logN)),您还可以使用BinarySearchBinarySearchFunc函数,这些函数也将随着这个新包一起提供。

最后,如果您希望将搜索时间保持为常数(O(1)),我建议采用**@tux21b**在被选答案中提到的使用映射的方法。

英文:

Complementing @Adolfo's answer (and other answers here), the Contains function won't be something experimental anymore. Starting with GO v1.21, which will be released during August 2023, the aforementioned slice package will be included into the core library (besides some other interesting packages like map and cmp), making it possible to run a linear search over a slice of elements (O(N) time complexity).

Additionally you will have some other interesting variations for searching an element (or elements) within a slice, like the new ContainsFunc, which reports whether at least one element e of s satisfies f(e). You can check this at the example below, which is taken from the actual docs:

package main

import (
	&quot;fmt&quot;
	&quot;slices&quot;
)

func main() {
	numbers := []int{0, 42, -10, 8}
	hasNegative := slices.ContainsFunc(numbers, func(n int) bool {
		return n &lt; 0
	})
	fmt.Println(&quot;Has a negative:&quot;, hasNegative)
	hasOdd := slices.ContainsFunc(numbers, func(n int) bool {
		return n%2 != 0
	})
	fmt.Println(&quot;Has an odd number:&quot;, hasOdd)
}

Also, to keep in mind, if you are working with a sorted slice and you want to reduce the time complexity when searching for an element (O(logN)), you will also be able to use the BinarySearch and the BinarySearchFunc functions, which will also come with this new package.

Finally, if you want to make the search constant in time (O(1)), I would go for the approach suggested by @tux21b in the voted answer by using maps.

答案15

得分: 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)
}
英文:

If you have a byte slice, you can use bytes package:

package main
import &quot;bytes&quot;

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

Or suffixarray package:

package main
import &quot;index/suffixarray&quot;

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

If you have an int slice, you can use intsets package:

package main
import &quot;golang.org/x/tools/container/intsets&quot;

func main() {
   var s intsets.Sparse
   for n := 10; n &lt; 20; n++ {
      s.Insert(n)
   }
   b := s.Has(16)
   println(b)
}

答案16

得分: 0

func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}

英文:
func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

答案17

得分: -1

我使用reflect包创建了以下的Contains函数。
这个函数可以用于各种类型,比如int32或struct等。

使用contains函数的示例如下:

// int32切片
containsInt32 := Contains([]int32{1, 2, 3, 4, 5}, 3)
fmt.Println("contains int32:", containsInt32)

// float64切片
containsFloat64 := Contains([]float64{1.1, 2.2, 3.3, 4.4, 5.5}, 4.4)
fmt.Println("contains float64:", containsFloat64)

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

// 输出:
// contains int32: true
// contains float64: true
// contains struct: true

更多详情请参考:
https://github.com/glassonion1/xgo/blob/main/contains.go

英文:

I created the following Contains function using reflect package.
This function can be used for various types like int32 or struct etc.

// 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 &lt; 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
}

Usage of contains function is below

// slice of int32
containsInt32 := Contains([]int32{1, 2, 3, 4, 5}, 3)
fmt.Println(&quot;contains int32:&quot;, containsInt32)

// slice of float64
containsFloat64 := Contains([]float64{1.1, 2.2, 3.3, 4.4, 5.5}, 4.4)
fmt.Println(&quot;contains float64:&quot;, containsFloat64)


// slice of struct
type item struct {
    ID   string
    Name string
}
list := []item{
    item{
        ID:   &quot;1&quot;,
        Name: &quot;test1&quot;,
    },
    item{
        ID:   &quot;2&quot;,
        Name: &quot;test2&quot;,
    },
    item{
        ID:   &quot;3&quot;,
        Name: &quot;test3&quot;,
    },
}
target := item{
	ID:   &quot;2&quot;,
	Name: &quot;test2&quot;,
}
containsStruct := Contains(list, target)
fmt.Println(&quot;contains struct:&quot;, containsStruct)

// Output:
// contains int32: true
// contains float64: true
// contains struct: true

Please see here for more details:
https://github.com/glassonion1/xgo/blob/main/contains.go

答案18

得分: -2

有几个可以帮助的包,但这个看起来很有前途:

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)
英文:

There are several packages that can help, but this one seems promising:

https://github.com/wesovilabs/koazee

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

答案19

得分: -3

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

例如,您有一个包含单词值的切片(例如“yes”,“no”,“maybe”)。这些结果被附加到一个切片中。如果您想检查该切片是否包含任何“maybe”结果,可以使用以下代码:

exSlice := ["yes", "no", "yes", "maybe"]
if strings.Contains(strings.Join(exSlice, ","), "maybe") {
  fmt.Println("我们有一个maybe!")
}

这个方法的适用性取决于切片的大小和其成员的长度。对于大型切片或长值,可能会存在性能或适用性问题,但对于较小的有限大小和简单值的切片,这是一个有效的一行代码来实现所需的结果。

英文:

It might be considered a bit 'hacky' but depending the size and contents of the slice, you can join the slice together and do a string search.

For example you have a slice containing single word values (e.g. "yes", "no", "maybe"). These results are appended to a slice. If you want to check if this slice contains any "maybe" results, you may use

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

How suitable this is really depends on the size of the slice and length of its members. There may be performance or suitability issues for large slices or long values, but for smaller slices of finite size and simple values it is a valid one-liner to achieve the desired result.

huangapple
  • 本文由 发表于 2012年5月8日 00:35:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/10485743.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定