duanhan8757 2019-05-31 11:54
浏览 89
已采纳

从Golang中的切片中删除

I have the following test that is printing the original input slice (after the filtering) without the element that has been removed, but with an extra element at the end making the input slice of the same length, even if after the filtering it should be shorter.

I've gone through this doc https://github.com/golang/go/wiki/SliceTricks#delete However I think I am missing some gotchas about Go, because it seems I am using slices with the wrong approach.

  • how can I avoid to have an "output slice"? (which is printed in the correct way, containing the right elements, having the expected length and capacity)
  • why my attempt at "removing inplace" leads to having the "input slice" with the same length as it had before the filtering process?
  • why the "input slice" has the same length as before I was applying the filtering process? How can I make the remove operation to change the length of the "input slice"?

This is the code:

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("
Original (PRE) slice
")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    idFilter := 1

    fePtr, outFooSlice := FindAndRemoveFromFooSlice(idFilter, inFooSlice)

    fmt.Printf("
Original (POST) slice
")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    fmt.Printf("
Filtered element
")
    fmt.Println(*fePtr)

    fmt.Printf("
Output slice
")
    fmt.Println(outFooSlice)
    fmt.Println(len(outFooSlice))
    fmt.Println(cap(outFooSlice))
}

This is the output of the test execution:

$ 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

Update on the "input slice as pointer"

OK, so assuming I would like to deal with the original input slice, i.e. no copy or output slice.

  • Why the following code throws a runtime panic in the commented line of code? (pointedInSl[inLen-1] = FooItem{})
  • Why the printed slice (after applying the function) contains 2 identical itmes at the end of it? How con I remove the last redundant element?
  • Why the length of the slice after applying the function is still the same as the one of the slice before applying the function?
  • How can I make the original slice shrink of 1 (i.e. being of output length = original length - 1)?

This is the code:

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("
Original (PRE) slice
")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    idFilter := 1

    fePtr := FindAndRemoveFromFooSliceInPlace(idFilter, &inFooSlice)

    fmt.Printf("
Original (POST) slice
")
    fmt.Println(inFooSlice)
    fmt.Println(len(inFooSlice))
    fmt.Println(cap(inFooSlice))

    fmt.Printf("
Filtered element
")
    fmt.Println(*fePtr)
}

This is the weird output:

$ 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
  • 写回答

2条回答 默认 最新

  • douchuo1963 2019-06-04 09:08
    关注

    I suggested an edit of icza answer to provide at its bottom a minimum working code example for the useful information he gave. It was rejected stating that it makes no sense as an edit and that it should have been written as a comment or an answer, so here it is (the credit goes mostly to icza):

    Minimum Working Code Example (with comments to give some context):

    // 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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?