如何从 Golang 中的切片中删除元素

How to delete an element from a Slice in Golang

提问人:Anchal Sarraf 提问时间:5/20/2016 最后编辑:CarsonAnchal Sarraf 更新时间:10/18/2023 访问量:616616

问:

fmt.Println("Enter position to delete::")
fmt.Scanln(&pos)

new_arr := make([]int, (len(arr) - 1))
k := 0
for i := 0; i < (len(arr) - 1); {
    if i != pos {
        new_arr[i] = arr[k]
        k++
        i++
    } else {
        k++
    }
}

for i := 0; i < (len(arr) - 1); i++ {
    fmt.Println(new_arr[i])
}

我正在使用此命令从 Slice 中删除元素,但它不起作用,请建议。

切片

评论

8赞 squiguy 5/20/2016
这是一本好书:blog.golang.org/go-slices-usage-and-internals
0赞 hkulekci 5/20/2016
还有这个:sitepoint.com/arrays-slices-basic-oop-go
2赞 blackgreen 5/8/2022
这个问题在 Go 1.18 和泛型中得到了进一步回答: 1 2

答:

91赞 user6169399 5/20/2016 #1

从 Slice 中删除一个元素(这称为“重新切片”):

package main

import (
    "fmt"
)

func RemoveIndex(s []int, index int) []int {
    return append(s[:index], s[index+1:]...)
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println(all) //[0 1 2 3 4 5 6 7 8 9]
    all = RemoveIndex(all, 5)
    fmt.Println(all) //[0 1 2 3 4 6 7 8 9]
}

评论

13赞 evanmcdonnal 5/20/2016
值得指出的是,这被称为“重新切片”,并且相对昂贵,尽管这是 Go 中惯用的执行方式。只是不要将其与从链表中删除节点之类的操作混淆,因为事实并非如此,如果您要经常这样做,尤其是对于大型集合,则应考虑避免它的替代设计。
0赞 5/20/2016
是的,这是 Golang 中的惯用方式,即使在 C/Assembly 中,从排序数组中删除一个随机元素也很昂贵,您需要将所有正确的元素向左移动(复制)一个位置。是的,在某些用例中,链表是从列表中删除随机元素的更好解决方案。
4赞 michael.schuett 7/26/2019
请注意,此方法会导致 all 被修改,现在 n 和 all 都指向同一基础数组的一部分。这很可能会导致代码中出现错误。
1赞 OhhhThatVarun 9/28/2019
收到此错误2019/09/28 19:46:25 http: panic serving 192.168.1.3:52817: runtime error: slice bounds out of range [7:5] goroutine 7 [running]:
7赞 Kasper Gyselinck 8/6/2021
你确定@STEEL?-->play.golang.org/p/IzaGUJI4qeG
0赞 GoGo 5/20/2016 #2

除非您关心内容,否则无需检查每个元素,并且可以使用切片附加。尝试一下

pos := 0
arr := []int{1, 2, 3, 4, 5, 6, 7, 9}
fmt.Println("input your position")
fmt.Scanln(&pos)
/* you need to check if negative input as well */
if (pos < len(arr)){
    arr = append(arr[:pos], arr[pos+1:]...)
} else {
    fmt.Println("position invalid")
}
474赞 T. Claverie 5/20/2016 #3

订单很重要

如果要保持数组的顺序,则必须将删除索引右侧的所有元素向左移动一个。希望这可以在 Golang 中轻松完成:

func remove(slice []int, s int) []int {
    return append(slice[:s], slice[s+1:]...)
}

但是,这是低效的,因为您最终可能会移动所有元素,这成本很高。

顺序并不重要

如果您不关心排序,您可以更快地将要删除的元素替换为切片末尾的元素,然后返回 n-1 第一个元素:

func remove(s []int, i int) []int {
    s[i] = s[len(s)-1]
    return s[:len(s)-1]
}

使用切片方法,清空 1 000 000 个元素的数组需要 224 秒,而使用这种方法只需要 0.06ns。

此答案不执行边界检查。它需要一个有效的索引作为输入。这意味着大于或等于初始值的负值或指数将导致 Go 恐慌。len(s)

切片和数组被索引为 0,删除数组的第 n 个元素意味着提供输入 n-1。若要删除第一个元素,请调用 remove(s, 0),若要删除第二个元素,请调用 remove(s, 1),依此类推。

评论

3赞 Brad Peabody 12/8/2016
嗯,不是真的。这:肯定将最后一个元素复制到索引处的元素。然后,返回不带最后一个元素的切片。那里有两个声明。s[i] = s[len(s)-1]ireturn s[:len(s)-1]
3赞 zenocon 10/22/2018
失败,len(arr) == 2 并且要删除的 elem 是最后一个:play.golang.org/p/WwD4PfUUjsM
8赞 T. Claverie 11/1/2018
@zenocon 在 Golang 中,数组是 0-index,这意味着长度为 2 的数组的有效索引是 0 和 1。实际上,此函数不检查数组的边界,并期望提供有效的索引。当 len(arr) == 2 时,有效参数为 01。其他任何事情都会触发越界访问,Go 会惊慌失措。
10赞 Vlad V 2/17/2020
添加此作为参考,对于顺序无关紧要的选项,最好使用 .如果您正在使用非原始数组,则尤其如此。如果您有指向某些内容的指针,最好在切片之前创建要删除的元素,这样基础数组中就不会有指针。这个答案很好地解释了原因。简而言之:在删除元素的位置移动最后一个元素后,在切片前将最后一个元素归零。s[len(s)-1], s[i] = 0, s[len(s)-1]nil
19赞 michael.schuett 4/19/2020
这是一个危险的答案,我猜已经在程序中引入了几百个错误,这些错误并不完全理解这修改了对切片的所有引用。如果您通读原始问题,也可以看到原始问题想要保留原始切片,这也是不正确的。
33赞 David 10/4/2016 #4

次要的一点(代码高尔夫),但在顺序无关紧要的情况下,您不需要交换值。只需用最后一个位置的副本覆盖要删除的数组位置,然后返回一个截断的数组。

func remove(s []int, i int) []int {
    s[i] = s[len(s)-1]
    return s[:len(s)-1]
}

同样的结果。

评论

15赞 Kent 2/10/2017
最易读的实现是将第一个元素复制到指定的索引中,然后返回一个只有最后 n-1 个元素的数组。s[i] = s[0]return s[1:]
4赞 stevenspiel 3/17/2018
@Kent解决方案的游乐场
5赞 Dave C 8/18/2019
@Kent do versus 的问题在于,如果切片稍后得到 ed 或删除与 s 混合在一起,后者的性能会好得多。后者保留切片容量,而前者则不然。s[1:]s[:len(s)-1]appendappend
1赞 ivarec 10/28/2019
如果使用此函数删除第 0 个元素,则会反转结果。
23赞 Oleksandr Mosur 3/28/2019 #5

摘自《Go 编程语言》一书

要从切片中间删除元素,请保留顺序 在其余元素中,使用“复制”滑动编号较高的元素 元素减少 1 以填补空白:

func remove(slice []int, i int) []int {
  copy(slice[i:], slice[i+1:])
  return slice[:len(slice)-1]
}

评论

7赞 michael.schuett 7/26/2019
请注意,此方法会导致修改传入的原始切片。
0赞 blackbox 2/12/2022
@eatingthenight可以修改后备数组,为什么不能删除呢?go.dev/play/p/9eQStgiqYYKappend
0赞 Tom Anderson 7/14/2022
@blackbox 这是不对的,append 不会修改后备数组。您的代码将 append 的值分配给后备数组,如果省略此步骤,后备数组将保持不变。与 go.dev/play/p/7EubNjwOFuD 比较s2 = append(s2, 2)s2
0赞 blackbox 8/6/2022
当容量大于长度时,@TomAnderson append 会修改后备数组。这是我的原始示例,没有将追加的结果分配给 s2 go.dev/play/p/mz095qeCnvL。下面是显式设置 go.dev/play/p/v0JR88kuDyB 更大容量的示例。这里有一个更深入的例子 go.dev/play/p/sj95rpeaJRB
0赞 Alex Glover 8/25/2022
@blackbox,如果从 go.dev/play/p/v0JR88kuDyB 中的 print 语句中删除 ,则 s2 似乎未更新。那么,是否可以肯定地说后备数组已更新,但切片未更新?:cap(s2)
3赞 Karl Doenitz 6/14/2019 #6

也许你可以试试这个方法:

// DelEleInSlice delete an element from slice by index
//  - arr: the reference of slice
//  - index: the index of element will be deleted
func DelEleInSlice(arr interface{}, index int) {
    vField := reflect.ValueOf(arr)
    value := vField.Elem()
    if value.Kind() == reflect.Slice || value.Kind() == reflect.Array {
        result := reflect.AppendSlice(value.Slice(0, index), value.Slice(index+1, value.Len()))
        value.Set(result)
    }
}

用法:

arrInt := []int{0, 1, 2, 3, 4, 5}
arrStr := []string{"0", "1", "2", "3", "4", "5"}
DelEleInSlice(&arrInt, 3)
DelEleInSlice(&arrStr, 4)
fmt.Println(arrInt)
fmt.Println(arrStr)

结果:

0, 1, 2, 4, 5
"0", "1", "2", "3", "5"

评论

1赞 michael.schuett 7/26/2019
可能是因为它不是惯用的,也不是针对问题所问的问题过度设计的。这是一种有趣的解决方法,但没有人应该使用它。
1赞 Edw590 12/21/2019
谢谢!实际上对我来说,使用界面效果很好,因为这似乎是通用的,也许
0赞 Edw590 11/11/2021
在学习了更多的围棋之后,我现在对此有一个疑问。这不应该包括那里的数组类型吗?数组的大小不能像在 C 中那样修改,所以这可能不起作用(至少对我来说没有)。我相信该函数可以减少到 2 行(这也会部分优化它 - 声明的变量更少,要做的操作更少):.var value reflect.Value = reflect.ValueOf(array).Elem(); value.Set(reflect.AppendSlice(value.Slice(0, index), value.Slice(index+1, value.Len())))
145赞 michael.schuett 7/26/2019 #7

这有点奇怪,但这里的大多数答案都是危险的,并且掩盖了他们实际在做什么。查看有关从切片中删除项目的原始问题,正在制作切片的副本,然后填充它。这可确保在切片在程序中传递时不会引入细微的错误。

以下是一些代码,比较了此线程和原始帖子中的用户答案。这里有一个 go playground 来摆弄这段代码。

基于追加的删除

package main

import (
    "fmt"
)

func RemoveIndex(s []int, index int) []int {
    return append(s[:index], s[index+1:]...)
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    removeIndex := RemoveIndex(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 6 7 8 9 9]
    fmt.Println("removeIndex: ", removeIndex) //[0 1 2 3 4 6 7 8 9]

    removeIndex[0] = 999
    fmt.Println("all: ", all) //[999 1 2 3 4 6 7 9 9]
    fmt.Println("removeIndex: ", removeIndex) //[999 1 2 3 4 6 7 8 9]
}

在上面的例子中,你可以看到我创建了一个切片,并用数字 0 到 9 手动填充它。然后,我们从所有索引中删除索引 5,并将其分配给删除索引。但是,当我们现在打印出来时,我们看到它也被修改了。这是因为切片是指向基础数组的指针。将其写出也会导致被修改,不同之处在于它更长,因为一个元素不再可从 中访问。接下来,我们更改一个值,我们可以看到也被修改了。Effective go 对此进行了更详细的介绍。removeIndexallallremoveIndexremoveIndexall

下面的例子我不会进入,但它为我们的目的做了同样的事情。只是说明使用复制没有什么不同。

package main

import (
    "fmt"
)

func RemoveCopy(slice []int, i int) []int {
    copy(slice[i:], slice[i+1:])
    return slice[:len(slice)-1]
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    removeCopy := RemoveCopy(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 6 7 8 9 9]
    fmt.Println("removeCopy: ", removeCopy) //[0 1 2 3 4 6 7 8 9]

    removeCopy[0] = 999
    fmt.Println("all: ", all) //[99 1 2 3 4 6 7 9 9]
    fmt.Println("removeCopy: ", removeCopy) //[999 1 2 3 4 6 7 8 9]
}

问题原文答案

查看原始问题,它不会修改要从中删除项目的切片。对于大多数访问此页面的人来说,使此线程中的原始答案成为迄今为止最好的答案。

package main

import (
    "fmt"
)

func OriginalRemoveIndex(arr []int, pos int) []int {
    new_arr := make([]int, (len(arr) - 1))
    k := 0
    for i := 0; i < (len(arr) - 1); {
        if i != pos {
            new_arr[i] = arr[k]
            k++
        } else {
            k++
        }
        i++
    }

    return new_arr
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    originalRemove := OriginalRemoveIndex(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    fmt.Println("originalRemove: ", originalRemove) //[0 1 2 3 4 6 7 8 9]

    originalRemove[0] = 999
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    fmt.Println("originalRemove: ", originalRemove) //[999 1 2 3 4 6 7 8 9]
}

正如你所看到的,这个输出的行为是大多数人所期望的,也可能是大多数人想要的。修改不会导致更改,删除索引和分配索引的操作也不会导致更改!匪夷所思!originalRemoveall

这段代码有点长,所以上面的内容可以改成这样。

正确答案

package main

import (
    "fmt"
)

func RemoveIndex(s []int, index int) []int {
    ret := make([]int, 0)
    ret = append(ret, s[:index]...)
    return append(ret, s[index+1:]...)
}

func main() {
    all := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    removeIndex := RemoveIndex(all, 5)

    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    fmt.Println("removeIndex: ", removeIndex) //[0 1 2 3 4 6 7 8 9]

    removeIndex[0] = 999
    fmt.Println("all: ", all) //[0 1 2 3 4 5 6 7 8 9]
    fmt.Println("removeIndex: ", removeIndex) //[999 1 2 3 4 6 7 8 9]
}

与原始的删除索引解决方案几乎相同,但是,我们在返回之前制作了一个新切片以追加到该切片。

评论

23赞 JessG 4/19/2020
这应该是问题的答案,因为它是唯一一个解释修改切片支持阵列的风险的答案
-4赞 user1858478 8/18/2019 #8

下面是带有指针的 Playground 示例。https://play.golang.org/p/uNpTKeCt0sH

package main

import (
    "fmt"
)

type t struct {
    a int
    b string
}

func (tt *t) String() string{
    return fmt.Sprintf("[%d %s]", tt.a, tt.b)
}

func remove(slice []*t, i int) []*t {
  copy(slice[i:], slice[i+1:])
  return slice[:len(slice)-1]
}

func main() {
    a := []*t{&t{1, "a"}, &t{2, "b"}, &t{3, "c"}, &t{4, "d"}, &t{5, "e"}, &t{6, "f"}}
    k := a[3]
    a = remove(a, 3)
    fmt.Printf("%v  ||  %v", a, k)
}
16赞 Madhan Ganesh 12/6/2019 #9

我采用以下方法来删除切片中的项目。这有助于提高其他人的可读性。而且也是不可变的。

func remove(items []string, item string) []string {
    newitems := []string{}

    for _, i := range items {
        if i != item {
            newitems = append(newitems, i)
        }
    }

    return newitems
}

评论

0赞 eexit 7/8/2020
我更喜欢这种方法,您实际上是在删除该项目的所有出现。
2赞 Dele 8/21/2022
如果您有超过 20k 或更多的记录,则无法扩展,因为您需要循环 n 次才能获得 - 线性搜索不会削减它。
0赞 Jon Nicholson 8/21/2023
正如 Dele 上面所说,这根本不是可扩展的,但它优雅而干净。在生产环境中,您可能不会使用这样的方法,但对于小型环境,这很好。
1赞 saad hafa 2/16/2020 #10

也许这段代码会有所帮助。

它删除具有给定索引的项目。

获取数组和要删除的索引,并返回一个新数组,与 append 函数非常相似。

func deleteItem(arr []int, index int) []int{
  if index < 0 || index >= len(arr){
    return []int{-1}
  }

    for i := index; i < len(arr) -1; i++{
      arr[i] = arr[i + 1]

    }

    return arr[:len(arr)-1]
}

在这里你可以玩代码:https://play.golang.org/p/aX1Qj40uTVs

31赞 Kemar H 6/10/2020 #11

这就是您如何以惯用方式切片中删除。您无需构建函数,它就内置在追加中。 在这里试试 https://play.golang.org/p/QMXn9-6gU5P

z := []int{9, 8, 7, 6, 5, 3, 2, 1, 0}
fmt.Println(z)  //will print Answer [9 8 7 6 5 3 2 1 0]

z = append(z[:2], z[4:]...)
fmt.Println(z)   //will print Answer [9 8 5 3 2 1 0]
14赞 TheName 9/4/2020 #12

最好的方法是使用 append 函数:

package main

import (
    "fmt"
)

func main() {
    x := []int{4, 5, 6, 7, 88}
    fmt.Println(x)
    x = append(x[:2], x[4:]...)//deletes 6 and 7
    fmt.Println(x)
}

https://play.golang.org/p/-EEFCsqse4u

5赞 Franci 9/18/2020 #13

在这里找到一条路,而无需搬迁。

  • 变更单
a := []string{"A", "B", "C", "D", "E"}
i := 2

// Remove the element at index i from a.
a[i] = a[len(a)-1] // Copy last element to index i.
a[len(a)-1] = ""   // Erase last element (write zero value).
a = a[:len(a)-1]   // Truncate slice.

fmt.Println(a) // [A B E D]
  • 保持秩序
a := []string{"A", "B", "C", "D", "E"}
i := 2

// Remove the element at index i from a.
copy(a[i:], a[i+1:]) // Shift a[i+1:] left one index.
a[len(a)-1] = ""     // Erase last element (write zero value).
a = a[:len(a)-1]     // Truncate slice.

fmt.Println(a) // [A B D E]
-3赞 Alex Punnen 3/5/2021 #14

由于 Slice 由数组支持,并且由于无法从数组中删除元素而不重新洗牌内存;我不想做那些丑陋的代码;这是一个伪代码,用于保留已删除项目的索引;基本上,我想要一个有序的切片,即使在删除后位置也很重要

type ListSlice struct {
  sortedArray []int
  deletedIndex map[int]bool
}
func lenSlice(m ListSlice)int{
    return len(m.sortedArray)
}
func deleteSliceElem(index int,m ListSlice){
    m.deletedIndex[index]=true
}
func getSliceElem(m ListSlice,i int)(int,bool){
    _,deleted :=m.deletedIndex[i]
    return m.sortedArray[i],deleted
}
for i := 0; i < lenSlice(sortedArray); i++ {
        
        k,deleted := getSliceElem(sortedArray,i)
        if deleted {continue}
        ....
        deleteSliceElem(i,sortedArray)

}

m := ListSlice{sortedArray: []int{5, 4, 3},deletedIndex: make(map[int]bool) }
...
8赞 Alberto Benegiamo 5/20/2021 #15

目前 T. Claverie 投票最多的答案是正确的,但我发现如果仅在需要时执行交换,即除了切片的最后一个元素之外的所有元素,该算法会更清楚。这可以通过一个简单的 if guard 来实现。

顺序不重要/没有边界检查

func remove(s []int, i int) []int {
    // bring element to remove at the end if it's not there yet
    if i != len(s)-1 {
        s[i] = s[len(s)-1]
    }
 
    // drop the last element
    return s[:len(s)-1]
}
1赞 Andrea Morandi 11/5/2021 #16

语言教程中,我们了解到:

切片类似于对数组的引用。 切片不存储任何数据,它只是描述底层数组的一部分。 更改切片的元素会修改其基础数组的相应元素。

出于这个原因,在不考虑我们正在处理的值的原点和目的地的情况下在切片上使用函数是非常危险的,也是对 Go 哲学来说错误的。append

因此,正确的解决方案是使用引用到新数组的切片,而不是“主”数组。 这可以通过在构造中创建一个新切片来实现。make

func removeAt(slice []int, index int) []int {
    newSlice := make([]int, 0) //Create a new slice of type []int and length 0
    newSlice = append(newSlice, slice[:index]...) //Copies the values contained in the old slice to the new slice up to the index (excluded)
    if index != len(slice)-1 {
        newSlice = append(newSlice, slice[index+1:]...) //If the index to be removed was different from the last one, then proceed to copy the following values of the index to the end of the old slice
    }
    return newSlice
}

通过这种方式,我们能够安全地删除切片的元素,无论我们在函数返回时将进行何种用途。


由于我使用了一个函数来回答问题,因此最好按如下方式处理任何错误:

func removeAt(slice []int, index int) ([]int, error) {
    if index < 0 {
        return nil, fmt.Errorf("index (%d) cannot be a negative number", index)
    }
    if index >= len(slice) {
        return nil, fmt.Errorf("index (%d) cannot be a number greater or equal than the length of slice (%d)", index, len(slice))
    }

    newSlice := make([]int, 0)
    newSlice = append(newSlice, slice[:index]...)
    if index != len(slice)-1 {
        newSlice = append(newSlice, slice[index+1:]...)
    }
    
    return newSlice, nil
}

或者更好的是,实现可以通过接口处理多种类型的函数。 但是,所有这些都是一个很好的做法,因为您构建了一个函数来执行此操作,这与提出的问题无关。

但是,可以在此处找到 Go 操场上的测试示例。

0赞 Highdeger 3/27/2022 #17

你需要稍微改变你的代码,

new_arr := make([]int, (len(arr) - 1))
for i := 0; i < len(arr); i++ {
    if i != pos {
        new_arr = append(new_arr, arr[i])
    }
}

为了获得更有效的循环,您可以使用它

for i, item := range arr {
    ...
}

最后,您可以使用本机切片功能来完成此操作

new_arr = append(arr[:2], arr[3:])

最后一个解决方案删除索引 2 中的元素并将新切片放入new_arr。

13赞 Kroksys 3/29/2022 #18

使用泛型可以传递切片类型。any

// Removes slice element at index(s) and returns new slice
func remove[T any](slice []T, s int) []T {
    return append(slice[:s], slice[s+1:]...)
}

用法

slice := []int{1, 2, 3, 4}
result := remove(slice, 0)
fmt.Println(result)
// [2 3 4]

示例
https://go.dev/play/p/LhPGvEuZbRA

54赞 Alex80286 3/31/2022 #19

使用软件包中的 Delete(自 Go 1.21 起稳定,在较旧的 Go 版本上您必须导入):slicesgolang.org/x/exp/slices

slice := []int{1, 2, 3, 4}
slice = slices.Delete(slice, 1, 2)
fmt.Println(slice) // [1 3 4]

Go playground 示例

slices.Delete(s, i, j)从 s 中删除元素 s[i:j]

  • 即从索引 i 到索引 i 的元素(包括索引 i)到索引 J(不包括索引 J)的元素
  • 或者,如果您还记得间隔的数学符号:[i,j]

请注意两件事:

  • Delete修改原始切片的内容
  • 您需要重新分配,否则它将具有错误的长度slice

评论

10赞 Tony Montana 6/6/2022
它是这个的包装函数。更多信息:cs.opensource.google/go/x/exp/+/0b5c67f0:slices/slices.go;l=156append(slice[:s], slice[s+1:]...)
1赞 Tom Böttger 12/13/2022
源代码指出“删除修改切片的内容;它不会创建新的切片“,但很明显,您需要重新分配修改后的切片来更新长度。这真的应该强调。
0赞 Alex80286 12/14/2022
@TomBöttger:请帮我改进我的答案,让我知道:“你需要重新分配切片,否则它的长度会错误”这句话有什么不够清楚的地方?
1赞 Tom Böttger 12/14/2022
@Alex80286 对不起,我的评论很模棱两可。你的答案完全没问题!你提到切片需要重新分配是件好事,因为 go 源代码没有提到它(至少没有直接提到)。
0赞 Michal Przybylowicz 8/14/2023
此函数不会修改切片的后备数组!这意味着“已删除”的元素仍在后备数组内。这与重新索引切片没有什么不同。
2赞 alimkhan 7007 12/2/2022 #20
for index, item := range movies{
    if item.ID == "123"{
        movies = append(movies[:index], movies[index+1:]...)
        break
    }
}
0赞 Jatinder Dhaliwal 4/21/2023 #21

这是从列表中删除元素的正确和最佳方法。因为其他方法可以在删除时移动元素。因此,如果顺序很重要,请尝试以下方法。

 func remove(s []int, index int) []int{{ return append(s[:index], s[index+1:]...) }
0赞 Artur Yukhatov 6/30/2023 #22

基本上,您只需将相同的切片分配给您的切片,但短一个项目:

数据 = 数据[:len(data)-1]

-1赞 Carson 8/31/2023 #23

这是用于按值删除项目

(去 1.21)

func sliceRemove[T comparable](s *[]T, rmItems ...T) {
    var end bool
    for {
        end = true
        for i, elem := range *s {
            for _, rmData := range rmItems {
                if elem == rmData {
                    *s = slices.Delete(*s, i, i+1)
                    end = false
                    break
                }
            }
            if !end {
                break
            }
        }
        if end {
            break
        }
    }
}

func Example_sliceRemove() {
    slice := []string{"Apple", "Bar", "Foo", "Foo", "Bar", "Banana"}
    sliceRemove(&slice, "Bar", "Foo")
    fmt.Println(slice)
    // Output:
    // [Apple Banana]
}

playground

banch标志

func sliceRemove[T comparable](s *[]T, rmItems ...T) {
    var end bool
    for {
        end = true
        for i, elem := range *s {
            for _, rmData := range rmItems {
                if elem == rmData {
                    *s = slices.Delete(*s, i, i+1)
                    end = false
                    break
                }
            }
            if !end {
                break
            }
        }
        if end {
            break
        }
    }
}

func BenchmarkSliceRemoveFast(b *testing.B) {
    slice := []int{1, 2, 3, 2, 3, 4}
    for N := 0; N < b.N; N++ {
        sliceRemove(&slice, 2, 3)
    }
}

func BenchmarkSliceRemoveSlow(b *testing.B) {
    slice := []int{1, 2, 3, 2, 3, 4}
    for N := 0; N < b.N; N++ {
        rmItems := []int{2, 3}
        var newSlice []int
        for _, e := range slice {
            if !slices.Contains(rmItems, e) {
                newSlice = append(newSlice, e)
            }
        }
    }
}
go test -v -bench=BenchmarkSliceRemove -run=none -benchmem

BenchmarkSliceRemoveFast
BenchmarkSliceRemoveFast-8      123802942                9.264 ns/op           0 B/op          0 allocs/op
BenchmarkSliceRemoveSlow
BenchmarkSliceRemoveSlow-8      19463425                89.18 ns/op           24 B/op          2 allocs/op
0赞 pkaramol 10/2/2023 #24

我在这里的两分钱,在引入库之后变得太强大了,因此:genericslodash

mySlice = lo.Without[T](myslice, mySlice[indexOfTheElementToRemove])

其中的类型(如果它不是自定义类型,我认为它是自动推断的,因此可能不需要)。TmySliceT