我有一个字符串数组,我想排除以foo_OR 开头的值超过7个字符.
我可以循环遍历每个元素,运行if语句,并将其添加到路径中的切片.但我很好奇是否有一种惯用的或更像golang的方式来完成它.
例如,同样的事情可能在Ruby中完成
my_array.select! { |val| val !~ /^foo_/ && val.length <= 7 }
Run Code Online (Sandbox Code Playgroud)
icz*_*cza 43
你没有在Ruby中使用过单行程序,但是使用帮助程序功能可以使它几乎一样短.
这是我们的辅助函数循环切片,并选择并仅返回符合函数值捕获的条件的元素:
func filter(ss []string, test func(string) bool) (ret []string) {
for _, s := range ss {
if test(s) {
ret = append(ret, s)
}
}
return
}
Run Code Online (Sandbox Code Playgroud)
使用这个辅助函数你的任务:
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)
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上试试):
[asdf nfoo_1]
Run Code Online (Sandbox Code Playgroud)
注意:
如果预计会选择许多元素,那么ret预先分配"大" 切片并使用简单赋值而不是使用简单赋值可能是有利可图的append().在返回之前,将切片的ret长度等于所选元素的数量.
笔记2:
在我的例子中,我选择了一个test()函数来告诉是否要返回一个元素.所以我不得不颠倒你的"排除"条件.显然,你可以编写辅助函数来期望一个测试器函数,它告诉你要排除什么(而不是要包含什么).
Elw*_*ens 13
看看robpike的过滤器库.这将允许您:
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]
}
Run Code Online (Sandbox Code Playgroud)
有趣的是Rob的README.md:
我想看看在Go中实现这种东西是多么困难,因为我能管理的API很好.这并不难.几年前写完之后,我没有机会使用它一次.相反,我只是使用"for"循环.你也不应该使用它.
因此根据Rob的最惯用的方式是:
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]
}
Run Code Online (Sandbox Code Playgroud)
这种风格与任何C系列语言采用的方法非常相似(如果不相同).
今天,我偶然发现了一个使我惊讶的漂亮成语。如果要在不分配的情况下就位过滤片,请使用具有相同支持数组的两个片:
s := []T{
// the input
}
s2 := s
s = s[:0]
for _, v := range s2 {
if shouldKeep(v) {
s = append(s, v)
}
}
Run Code Online (Sandbox Code Playgroud)
这是删除重复字符串的特定示例:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
如果您需要保留两个切片,则只需将其替换s = s[:0]为s = nil或s = make([]T, 0, len(s)),这取决于您是否要append()为您分配。
有几种不错的方法可以在没有分配或新依赖项的情况下过滤切片。在 Github 上的 Go wiki 中找到:
过滤器(就地)
Run Code Online (Sandbox Code Playgroud)n := 0 for _, x := range a { if keep(x) { a[n] = x n++ } } a = a[:n]
另一种更具可读性的方式:
过滤而不分配
这个技巧利用了一个事实,即切片与原始切片共享相同的后备数组和容量,因此存储被重新用于过滤切片。当然,对原始内容进行了修改。
Run Code Online (Sandbox Code Playgroud)b := a[:0] for _, x := range a { if f(x) { b = append(b, x) } }对于必须被垃圾收集的元素,可以在之后包含以下代码:
Run Code Online (Sandbox Code Playgroud)for i := len(b); i < len(a); i++ { a[i] = nil // or the zero value of T }
我不确定的一件事是,第一种方法是否需要清除(设置为nil)aindex 之后slice 中的项目n,就像它们在第二种方法中所做的那样。
编辑:第二种方式基本上是 MicahStetson 在他的回答中所描述的。在我的代码中,我使用了一个类似于以下的函数,它在性能和可读性方面可能与它一样好:
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
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果切片中的项目不是指针并且不包含指针,则可以跳过第二个 for 循环。