删除切片中的元素

Jor*_*ero 136 go

func main() {
    a := []string{"Hello1", "Hello2", "Hello3"}
    fmt.Println(a)
    // [Hello1 Hello2 Hello3]
    a = append(a[:0], a[1:]...)
    fmt.Println(a)
    // [Hello2 Hello3]
}
Run Code Online (Sandbox Code Playgroud)

这个删除技巧如何与append函数一起工作?

似乎它在第一个元素(空数组)之前抓住了所有东西

然后在第一个元素(位置零)之后追加所有内容

......(点点)是做什么的?

dav*_*ave 268

a切片在哪里,并且i是要删除的元素的索引:

a = append(a[:i], a[i+1:]...)
Run Code Online (Sandbox Code Playgroud)

... 是Go中可变参数的语法.

基本上,在定义函数时,它会将您传递的所有参数放入该类型的一个切片中.通过这样做,您可以根据需要传递任意数量的参数(例如,fmt.Println可以根据需要使用尽可能多的参数).

现在,在调用函数时,...执行相反的操作:它解压缩一个切片并将它们作为单独的参数传递给一个可变参数函数.

那么这一行是做什么的:

a = append(a[:0], a[1:]...)
Run Code Online (Sandbox Code Playgroud)

基本上是:

a = append(a[:0], a[1], a[2])
Run Code Online (Sandbox Code Playgroud)

现在,你可能想知道,为什么不做

a = append(a[1:]...)
Run Code Online (Sandbox Code Playgroud)

好吧,功能定义append

func append(slice []Type, elems ...Type) []Type
Run Code Online (Sandbox Code Playgroud)

所以第一个参数必须是正确类型的一个切片,第二个参数是可变参数,所以我们传入一个空切片,然后解压缩切片的其余部分以填充参数.

  • 如果我是切片中的最后一个元素,你不会超出范围异常吗?``a =追加(a [:i],a [i + 1:] ...)`` (30认同)
  • @Tyguy7我想你试图在循环中删除切片的元素.所以你必须小心索引. (6认同)
  • @DaveC我在项目中使用切片时遇到错误:/ (5认同)
  • 来自规范的@Tyguy7:"对于数组或字符串,如果0 <=低<=高<= len(a),则索引在范围内,否则它们超出范围." 也许在你的情况下高<低; 在这种情况下,你会得到错误.(https://golang.org/ref/spec#Slice_expressions) (3认同)
  • 这个表现如何?我非常希望它不会在引擎盖下创造一个全新的片段。 (2认同)

Chr*_*ris 42

有两种选择:

答:您关心保留数组顺序:

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]
Run Code Online (Sandbox Code Playgroud)

B:你不关心保留订单(这可能更快):

a[i] = a[len(a)-1] // Replace it with the last one. CAREFUL only works if you have enough elements.
a = a[:len(a)-1]   // Chop off the last one.
Run Code Online (Sandbox Code Playgroud)

如果您的数组是指针,请参阅链接以查看内存泄漏的含义.

https://github.com/golang/go/wiki/SliceTricks

  • 只是抬起头,如果您尝试删除切片中的最后一个元素,则尝试使用b中的第一个元素(用最后一个元素替换)显然不起作用 (2认同)

Zyl*_*Zyl 10

不要将[a:]-,[:b]-和- [a:b]标记中的索引视为元素索引,而应将它们视为元素周围和元素之间的间隙的索引,从0在元素索引为之前的间隙索引开始0

在此处输入图片说明

仅查看蓝色数字,就更容易了解发生了什么:将[0:3]所有内容围起来,[3:3]为空并[1:2]产生yield {"B"}。然后[a:]是的短版[a:len(arrayOrSlice)][:b]的短版[0:b][:]的短版[0:len(arrayOrSlice)]。后者通常用于在需要时将数组变成切片。


fra*_*lin 5

...是可变参数的语法.

我认为它是由编译器使用slice实现的([]Type)就像函数append:

func append(slice []Type, elems ...Type) []Type
Run Code Online (Sandbox Code Playgroud)

当你在"追加"中使用"elems"时,实际上它是一个切片([]类型).所以" a = append(a[:0], a[1:]...)"的意思是" a = append(a[0:0], a[1:])"

a[0:0] 是一片什么都没有

a[1:] 是"Hello2 Hello3"

这是它的工作原理

  • `a [0:0]`不是'nil`而是长度为0的切片.如果`a`是'nil`,`a [0:0]`将**仅**为'nil`. (2认同)

小智 5

我在接受的答案解决方案中得到一个索引超出范围的错误。原因:range start时不是一一迭代value,而是index迭代。如果在范围内修改切片,则会引发一些问题。

旧答案:

chars := []string{"a", "a", "b"}

for i, v := range chars {
    fmt.Printf("%+v, %d, %s\n", chars, i, v)
    if v == "a" {
        chars = append(chars[:i], chars[i+1:]...)
    }
}
fmt.Printf("%+v", chars)
Run Code Online (Sandbox Code Playgroud)

预期的 :

[a a b], 0, a
[a b], 0, a
[b], 0, b
Result: [b]
Run Code Online (Sandbox Code Playgroud)

实际的:

// Autual
[a a b], 0, a
[a b], 1, b
[a b], 2, b
Result: [a b]
Run Code Online (Sandbox Code Playgroud)

正确方法(解决方案):

chars := []string{"a", "a", "b"}

for i := 0; i < len(chars); i++ {
    if chars[i] == "a" {
        chars = append(chars[:i], chars[i+1:]...)
        i-- // form the remove item index to start iterate next item
    }
}

fmt.Printf("%+v", chars)
Run Code Online (Sandbox Code Playgroud)

来源:https : //dinolai.com/notes/golang/golang-delete-slice-item-in-range-problem.html