我有一个字符串数组,我想排除以
我可以遍历每个元素,运行
例如,在Ruby中可能会完成与
1 | my_array.select! { |val| val !~ /^foo_/ && val.length <= 7 } |
没有像Ruby中那样的单行代码,但是借助辅助函数,您可以使其变得几乎一样短。
这是我们的辅助函数,该函数遍历一个切片,并仅选择并返回满足由函数值捕获的条件的元素:
1 2 3 4 5 6 7 8 | func filter(ss []string, test func(string) bool) (ret []string) { for _, s := range ss { if test(s) { ret = append(ret, s) } } return } |
使用此帮助器功能,您的任务:
1 2 3 4 5 6 | ss := []string{"foo_1","asdf","loooooooong","nfoo_1","foo_2"} mytest := func(s string) bool { return !strings.HasPrefix(s,"foo_") && len(s) <= 7 } s2 := filter(ss, mytest) fmt.Println(s2) |
输出(在Go Playground上尝试):
1 | [asdf nfoo_1] |
注意:
如果预期将选择许多元素,则预先分配一个"大"
笔记2:
在我的示例中,我选择了
看看robpike的过滤器库。这将允许您执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package main import ( "fmt" "strings" "filter" ) func isNoFoo7(a string) bool { return ! strings.HasPrefix(a,"foo_") && len(a) <= 7 } func main() { a := []string{"test","some_other_test","foo_etc"} result := Choose(a, isNoFoo7) fmt.Println(result) // [test] } |
有趣的是Rob的README.md:
I wanted to see how hard it was to implement this sort of thing in Go, with as nice an API as I could manage. It wasn't hard.
Having written it a couple of years ago, I haven't had occasion to use it once. Instead, I just use"for" loops.
You shouldn't use it either.
因此,Rob所说的最惯用的方式是:
1 2 3 4 5 6 7 8 9 10 | func main() { a := []string{"test","some_other_test","foo_etc"} nofoos := []string{} for i := range a { if(!strings.HasPrefix(a[i],"foo_") && len(a[i]) <= 7) { nofoos = append(nofoos, a[i]) } } fmt.Println(nofoos) // [test] } |
这种风格与任何C族语言所采用的方法都非常相似(甚至不同)。
-
我认为for循环更像这样:
for _, elt := range a { if(!strings.HasPrefix(elt,"foo_") && len(elt) <= 7) { nofoos = append(nofoos, elt) } }
今天,我偶然发现了一个使我惊讶的漂亮成语。如果要在不分配的情况下就位过滤片,请使用具有相同支持数组的两个片:
1 2 3 4 5 6 7 8 9 10 | s := []T{ // the input } s2 := s s = s[:0] for _, v := range s2 { if shouldKeep(v) { s = append(s, v) } } |
这是删除重复字符串的特定示例:
1 2 3 4 5 6 7 8 9 10 | s := []string{"a","a","b","c","c"} s2 := s s = s[:0] var last string for _, v := range s2 { if len(s) == 0 || v != last { last = v s = append(s, v) } } |
如果需要保留两个切片,只需将
- 这是一个经典的把戏。 s = s [:0]保留基础的数组和切片容量,仅将切片长度清零。
在Go中,没有一种惯用的方法可以在一行中获得与Ruby中相同的预期结果,但是通过一个辅助函数,您可以获得与Ruby中相同的表现力。
您可以将该助手功能称为:
1 2 3 | Filter(strs, func(v string) bool { return strings.HasPrefix(v,"foo_") // return foo_testfor })) |
这是完整的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package main import"strings" import"fmt" // Returns a new slice containing all strings in the // slice that satisfy the predicate `f`. func Filter(vs []string, f func(string) bool) []string { vsf := make([]string, 0) for _, v := range vs { if f(v) && len(v) > 7 { vsf = append(vsf, v) } } return vsf } func main() { var strs = []string{"foo1","foo2","foo3","foo3","foo_testfor","_foo"} fmt.Println(Filter(strs, func(v string) bool { return strings.HasPrefix(v,"foo_") // return foo_testfor })) } |
运行示例:操场
有两种不错的方法可以在没有分配或没有新依赖项的情况下过滤片。在Github的Go Wiki中找到:
Filter (in place)
1
2
3
4
5
6
7
8
9
10 n := 0
for _, x := range a {
if keep(x) {
a[n] = x
n++
}
}
a = a[:n]
另一种更易读的方式:
Filtering without allocating
This trick uses the fact that a slice shares the same backing array
and capacity as the original, so the storage is reused for the
filtered slice. Of course, the original contents are modified.
1
2
3
4
5
6
7 b := a[:0]
for _, x := range a {
if f(x) {
b = append(b, x)
}
}For elements which must be garbage collected, the following code can
be included afterwards:
1
2
3 for i := len(b); i < len(a); i++ {
a[i] = nil // or the zero value of T
}
我不确定的一件事是,第一种方法是否需要清除索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | func filterSlice(slice []*T, keep func(*T) bool) []*T { newSlice := slice[:0] for _, item := range slice { if keep(item) { newSlice = append(newSlice, item) } } // make sure discarded items can be garbage collected for i := len(newSlice); i < len(slice); i++ { slice[i] = nil } return newSlice } |
请注意,如果切片中的项目不是指针并且不包含指针,则可以跳过第二个for循环。
看看这个库:github.com/thoas/go-funk
它在Go中提供了许多救生习惯的实现(例如,包括过滤数组中的元素)。
1 2 3 | r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 } |
"从数组中选择元素"通常也称为过滤器功能。没有这样的事情了。也没有其他"集合函数",例如map或reduce。对于获得期望结果的最惯用方式,我发现https://gobyexample.com/collection-functions是一个很好的参考:
[...] in Go it’s common to provide collection functions if and when they are specifically needed for your program and data types.
它们提供了字符串过滤器功能的实现示例:
1 2 3 4 5 6 7 8 9 | func Filter(vs []string, f func(string) bool) []string { vsf := make([]string, 0) for _, v := range vs { if f(v) { vsf = append(vsf, v) } } return vsf } |
但是,他们还说,仅内联函数通常是可以的:
Note that in some cases it may be clearest to just inline the
collection-manipulating code directly, instead of creating and calling
a helper function.
通常,golang尝试仅引入正交概念,这意味着当您可以以一种方式解决问题时,应该没有太多其他方式可以解决问题。通过仅具有一些核心概念,这增加了语言的简化性,因此并非每个开发人员都使用该语言的不同子集。