我进行了以下测试,即打印原始输入切片(过滤后),而没有删除元素,但是最后有一个额外的元素,使输入切片具有相同的长度,即使过滤后应该较短。
我已经看过这篇文档https://github.com/golang/go/wiki/SliceTricks#delete 但是我认为我缺少有关Go的一些技巧,因为似乎我使用的切片方法错误。
这是代码:
package foo import ( "fmt" "log" "math/rand" "testing" ) type FooItem struct { Id int Category string Value float64 } const minRand = 0 const maxRand = 10 const maxSliceLen = 3 var inFooSlice []FooItem func init() { for i := 1; i <= maxSliceLen; i++ { inFooSlice = append(inFooSlice, FooItem{ Id: i, Category: "FooCat", Value: minRand + rand.Float64()*(maxRand-minRand), }) } } // this is the function I am testing func FindAndRemoveFromFooSlice(iFilter int, inSl []FooItem) (*FooItem, []FooItem) { inLen := len(inSl) outSl := make([]FooItem, inLen) for idx, elem := range inSl { if elem.Id == iFilter { log.Printf("Loop ID %v", idx) // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete outSl = inSl[:idx+copy(inSl[idx:], inSl[idx+1:inLen])] outSl = outSl[:inLen-1] return &elem, outSl } } return nil, nil } func TestFoo(t *testing.T) { fmt.Printf("\nOriginal (PRE) slice\n") fmt.Println(inFooSlice) fmt.Println(len(inFooSlice)) fmt.Println(cap(inFooSlice)) idFilter := 1 fePtr, outFooSlice := FindAndRemoveFromFooSlice(idFilter, inFooSlice) fmt.Printf("\nOriginal (POST) slice\n") fmt.Println(inFooSlice) fmt.Println(len(inFooSlice)) fmt.Println(cap(inFooSlice)) fmt.Printf("\nFiltered element\n") fmt.Println(*fePtr) fmt.Printf("\nOutput slice\n") fmt.Println(outFooSlice) fmt.Println(len(outFooSlice)) fmt.Println(cap(outFooSlice)) }
这是测试执行的输出:
$ go test -v -run TestFoo === RUN TestFoo Original (PRE) slice [{1 FooCat 6.046602879796196} {2 FooCat 9.405090880450125} {3 FooCat 6.645600532184904}] 3 4 2019/05/31 12:53:30 Loop ID 0 Original (POST) slice [{2 FooCat 9.405090880450125} {3 FooCat 6.645600532184904} {3 FooCat 6.645600532184904}] 3 4 Filtered element {1 FooCat 6.046602879796196} Output slice [{2 FooCat 9.405090880450125} {3 FooCat 6.645600532184904}] 2 4 --- PASS: TestFoo (0.00s) PASS ok git.openenergi.net/scm/flex/service/common 0.008s
更新“输入切片作为指针”
OK,假设我要处理原始输入切片,即没有副本或输出切片。
pointedInSl[inLen-1] = FooItem{}
func FindAndRemoveFromFooSliceInPlace(iFilter int, inSl *[]FooItem) *FooItem { pointedInSl := *inSl inLen := len(pointedInSl) for idx, elem := range pointedInSl { if elem.Id == iFilter { log.Printf("Loop ID %v", idx) // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete pointedInSl = append(pointedInSl[:idx], pointedInSl[idx+1:inLen]...) // pointedInSl[inLen-1] = FooItem{} // why this throws a runtime "panic: runtime error: index out of range" ??? pointedInSl = pointedInSl[:inLen-1] return &elem } } return nil } func TestFooInPlace(t *testing.T) { fmt.Printf("\nOriginal (PRE) slice\n") fmt.Println(inFooSlice) fmt.Println(len(inFooSlice)) fmt.Println(cap(inFooSlice)) idFilter := 1 fePtr := FindAndRemoveFromFooSliceInPlace(idFilter, &inFooSlice) fmt.Printf("\nOriginal (POST) slice\n") fmt.Println(inFooSlice) fmt.Println(len(inFooSlice)) fmt.Println(cap(inFooSlice)) fmt.Printf("\nFiltered element\n") fmt.Println(*fePtr) }
这是奇怪的输出:
$ go test -v -run TestFooInPlace === RUN TestFooInPlace Original (PRE) slice [{1 FooCat 6.046602879796196} {2 FooCat 9.405090880450125} {3 FooCat 6.645600532184904}] 3 4 2019/05/31 16:32:38 Loop ID 0 Original (POST) slice [{2 FooCat 9.405090880450125} {3 FooCat 6.645600532184904} {3 FooCat 6.645600532184904}] 3 4 Filtered element {1 FooCat 6.046602879796196} --- PASS: TestFooInPlace (0.00s) PASS ok git.openenergi.net/scm/flex/service/common 0.007s
我建议对icza答案进行编辑,以在其底部提供一个最小的工作代码示例,以说明他提供的有用信息。被拒绝,指出它作为编辑是没有意义的,它应该被写为评论或答案,所以就在这里(主要归功于icza):
最低工作代码示例(带注释的内容):
// use a pointer for the input slice so then it is changed in-place func FindAndRemoveFromFooSliceInPlace(iFilter int, inSl *[]FooItem) *FooItem { pointedInSl := *inSl // dereference the pointer so then we can use `append` inLen := len(pointedInSl) for idx, elem := range pointedInSl { if elem.Id == iFilter { log.Printf("Loop ID %v", idx) // check these docs: https://github.com/golang/go/wiki/SliceTricks#delete pointedInSl = append(pointedInSl[:idx], pointedInSl[idx+1:inLen]...) pointedInSl = pointedInSl[:inLen-1] *inSl = pointedInSl // assigning the new slice to the pointed value before returning return &elem } } return nil }