提问人:vosmith 提问时间:5/8/2012 最后编辑:vosmith 更新时间:9/20/2023 访问量:662275
包含切片的方法
Contains method for a slice
答:
如果对切片进行了排序,则在排序
包中实现了二进制搜索。
不,这样的方法不存在,但写起来很琐碎:
func contains(s []int, e int) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
如果查找是代码的重要组成部分,则可以使用地图,但地图也有成本。
评论
contains
Mostafa 已经指出,这样的方法编写起来很简单,mkb 给了你一个提示,让你使用排序包中的二进制搜索。但是,如果您要进行大量此类包含检查,也可以考虑使用地图。
使用惯用语检查特定地图键是否存在是微不足道的。由于你对这个值不感兴趣,你也可以创建一个 例如。在这里使用空的优点是它不需要任何额外的空间,并且 Go 的内部地图类型针对这种值进行了优化。因此,是围棋世界中套装的热门选择。value, ok := yourmap[key]
map[string]struct{}
struct{}
map[string] struct{}
评论
struct{}{}
map[string] bool
map[string] struct{}
map[string] struct{}
struct {}{}
而不是使用 ,可能是更好的解决方案。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))
}
http://play.golang.org/p/CEG6cu4JTf
评论
sliceToMap
您可以使用 reflect 包来迭代具体类型为 slice 的接口:
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
}
评论
Contains()
List<T>
Equals()
如果使用地图根据键查找物品不可行,则可以考虑使用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 ./...
此方法将为 deriveContains 生成:
func deriveContainsFoo(list []Foo, item Foo) bool {
for _, v := range list {
if v == item {
return true
}
}
return false
}
Goderive 还支持其他一些有用的辅助方法,以在 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))
评论
O(n)
O(n*log(n))
contains
O(log(n))
O(n*log(n))
围棋风格:
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"
})
评论
它可能被认为有点“hacky”,但根据切片的大小和内容,您可以将切片连接在一起并进行字符串搜索。
例如,您有一个包含单个单词值的切片(例如“yes”、“no”、“maybe”)。这些结果将追加到切片中。如果要检查此切片是否包含任何“可能”结果,可以使用
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"]
","+strings.Join(exSlice,",")+","
",maybe,"
我认为比.map[x]bool
map[x]struct{}
为不存在的项目编制地图索引将返回 。所以,你可以说 。false
_, ok := m[X]
m[X]
这样可以很容易地在表达式中嵌套包含测试。
评论
我使用 reflect 包创建了以下 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
如果你有切片,你可以使用包: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://golang.org/pkg/bytes
- https://golang.org/pkg/index/suffixarray
- https://pkg.go.dev/golang.org/x/tools/container/intsets
有几个软件包可以提供帮助,但这个软件包似乎很有前途:
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.21 开始,您可以使用从前面提到的实验包升级的 stdlib 切片
包。
import "slices"
things := []string{"foo", "bar", "baz"}
slices.Contains(things, "foo") // true
原文答案:
从 Go 1.18 开始,您可以使用软件包——特别是通用函数:https://pkg.go.dev/golang.org/x/exp/slices#Contains。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
请注意,由于它作为实验包在 stdlib 之外,因此它不受 Go 1 兼容性承诺™的约束,并且在正式添加到 stdlib 之前可能会更改。
评论
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}
作为对 @Adolfo 的回答(以及此处的其他答案)的补充,Contains 函数将不再是实验性的。从将于 2023 年 8 月发布的 GO v1.21 开始,上述切片包将包含在核心库中(除了其他一些有趣的包,如 和 ),从而可以对元素切片
(O(N) 时间复杂度)运行线性搜索。map
cmp
此外,您还将有一些其他有趣的变体来搜索切片中的一个或多个元素,例如新的 ContainsFunc,它报告 s 的至少一个元素是否满足 。您可以在下面的示例中检查这一点,该示例取自实际文档:e
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)) 时降低时间复杂度,您还可以使用 BinarySearch
和 BinarySearchFunc
函数,它们也将随此新包一起提供。
最后,如果你想使搜索在时间上保持不变(O(1)),我会选择@tux21b在投票的答案中建议的方法,即使用地图。
下一个:检查两个切片的相等性
评论