如何从 Golang 的 Slice 中删除元素

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])
}

我使用这个命令来删除一个切片元素,但它不工作,请建议。

480310 次浏览

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

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]
}

没有必要检查每一个元素,除非你关心的内容,你可以利用切片追加。试试吧

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")
}

顺序很重要

如果你想要保持数组的有序,你必须将删除索引右边的所有元素向左移动一个单位。希望在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]
}

使用reslicing方法,清空一个包含100万个元素的数组需要224秒,而这个方法只需要0.06ns。

这个答案不执行范围检查。它需要一个有效的索引作为输入。这意味着大于或等于初始len(s)的负值或下标将导致Go panic。

切片和数组为0索引,删除数组的第n个元素意味着提供输入n - 1。要移除第一个元素,调用删除(年代,0),要移除第二个元素,调用删除(年代,1),以此类推。

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

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

相同的结果。

从书Go编程语言

从切片的中间删除一个元素,保持顺序 对于其余的元素,使用copy来滑动编号较高的元素

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

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

// 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"

这看起来有点奇怪,但这里的大多数答案都很危险,掩盖了他们实际在做的事情。看看最初关于从切片中移除一个项目的问题切片的副本正在制作,然后被填充。这可以确保当切片在程序中传递时,不会引入微妙的错误。

这里有一些代码比较用户在这个帖子和原始帖子中的回答。这里有一个去操场来处理这段代码。

基于追加的删除

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。然后从all中删除索引5,并将其赋值为remove index。然而,当我们现在打印出来的时候,我们看到它也被修改了。这是因为片是指向底层数组的指针。将其写入removeIndex也会导致all被修改,区别是allremoveIndex长了一个元素,从removeIndex无法再到达这个元素。接下来,我们改变了removeIndex中的一个值,我们可以看到all也被修改了。有效的去对此有更详细的介绍。

下面的例子我就不细讲了,但它和我们的目的是一样的。只是说明了使用copy没有什么不同。

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]
}

正如您所看到的,这个输出的行为是大多数人所期望的,可能是大多数人想要的。对originalRemove的修改不会导致all的变化,删除索引并为其赋值的操作也不会导致变化!太棒了!

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

正确答案

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 9 9]
fmt.Println("removeIndex: ", removeIndex) //[999 1 2 3 4 6 7 8 9]
}

几乎与原来的删除索引解决方案相同,但是我们在返回之前添加了一个新的切片。

这里是游乐场的例子,里面有指针。 https://play.golang.org/p/uNpTKeCt0sH < / p >

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)
}

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

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


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


return newitems
}

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

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

接受数组和要删除的索引,并返回一个新数组,类似于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

这就是你如何从 惯用的方法删除。你不需要构建一个函数,它被构建到附加中。 试试这里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]

最好的方法是使用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

找到一种方法在这里没有搬迁。

  • 更改订单
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]

因为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) }
...

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

秩序不重要/没有进行边界检查

func remove(s []int, i int) []int {
// bring element to remove at the end if its not there yet
if i != len(s)-1 {
s[i] = s[len(s)-1]
}
 

// drop the last element
return s[:len(s)-1]
}

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

片就像数组的引用。 片不存储任何数据,它只是描述底层数组的一部分。 改变片的元素将修改其底层数组中相应的元素

因此,在切片不考虑我们所处理的值的原点和终点上使用append函数对于Go哲学来说是非常危险的,也是错误的。

因此,正确的解决方案是使用引用到新数组的slice,而不是“;main"一个。 这可以通过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
}

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

然而,一个在围棋操场上测试的例子可以在在这里中找到。

你需要稍微修改一下你的代码,

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中。

使用泛型可以传递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]
< p >
示例 https://go.dev/play/p/LhPGvEuZbRA < / p >

使用slices包中的Delete(在Go 1.18中是实验性的,计划在Go 1.19中稳定):

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

去游乐场的例子

slices.Delete(s, i, j)从s中删除元素s[i:j]。 注意两点:

  • Delete修改原始片的内容
  • 删除索引i到j的元素
  • 你需要重新分配slice,否则它会有错误的长度
for index, item := range movies{
if item.ID == "123"{
movies = append(movies[:index], movies[index+1:]...)
break
}
}