如何从Golang中的Slice中删除元素

时间:2016-05-19 21:15:38

标签: go

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中删除一个元素,但它不起作用,请建议。

16 个答案:

答案 0 :(得分:71)

订单重要

如果要保持数组的有序,则必须将删除索引右侧的所有元素向左移动一个。希望这可以在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[len(s)-1], s[i] = s[i], s[len(s)-1]
    return s[:len(s)-1]
}

使用重新分割方法,清空1 000 000个元素的数组需要224秒,这个只需要0.06ns。我怀疑在内部,只改变切片的长度,而不修改它。

修改1

基于以下评论的快速笔记(感谢他们!)。

由于目的是删除一个元素,当订单无关紧要时,需要进行一次交换,第二个将被浪费:

func remove(s []int, i int) []int {
    s[i] = s[len(s)-1]
    // We do not need to put s[i] at the end, as it will be discarded anyway
    return s[:len(s)-1]
}

此外,此答案不会执行边界检查。它期望有效的索引作为输入。这意味着大于或等于len的负值或指数将导致Go恐慌。切片和数组被0索引,删除数组的第n个元素意味着提供输入 n-1 。要删除第一个元素,请调用删除(s,0),删除第二个元素,调用删除(s,1),依此类推。

答案 1 :(得分:19)

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

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

同样的结果。

答案 2 :(得分:12)

这是您切片惯用方式 删除的方式。您不需要构建附加在附件中的函数。 在这里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]

答案 3 :(得分:10)

从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]
    n := RemoveIndex(all, 5)
    fmt.Println(n) //[0 1 2 3 4 6 7 8 9]
}

答案 4 :(得分:2)

最好的方法是使用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 :(得分:2)

找到一种here而无需重新定位的方式。

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

答案 6 :(得分:1)

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

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

答案 7 :(得分:1)

摘自The Go Programming Language

  

要从切片中间删除元素,请保留顺序   剩余元素中的所有元素,请使用“复制”滑动编号较高的元素   元素向下缩小以填补空白:

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

答案 8 :(得分: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"

答案 9 :(得分:1)

看到这个有点奇怪,但是这里的大多数答案都是危险的,掩盖了他们实际所做的事情。查看最初提出的有关从切片中删除项目的问题,正在制作切片的副本,然后将其填充。这样可以确保在将切片传递给程序时不会引入细微的错误。

这是一些代码,用于比较用户在此主题中的答案和原始帖子。这是一个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,并将其分配为删除索引。但是,当我们现在打印全部内容时,我们发现它也已被修改。这是因为切片是指向基础数组的指针。将其写出到removeIndex也会导致all被修改,区别是all的长度增加了一个元素,而该元素无法从removeIndex到达。接下来,我们在removeIndex中更改一个值,我们可以看到all也被修改了。 Effective go对此进行了更详细的介绍。

下面的示例我不会涉及,但出于我们的目的,它做了同样的事情。只是说明使用副本没有什么不同。

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

几乎与原始的remove index解决方案相同,但是我们在返回之前制作了一个要附加到的新切片。

答案 10 :(得分:1)

我采用以下方法删除切片中的项目。这有助于他人阅读。而且也是一成不变的。

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

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

    return newitems
}

答案 11 :(得分:1)

也许此代码会有所帮助。

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

获取数组,删除要删除的索引并返回一个新数组,就像添加函数一样。

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

答案 12 :(得分:0)

要从切片中间删除元素,请保留顺序 剩余元素中的所有元素,请使用copy滑动编号较高的元素 元素向下缩小以填补空白:

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

如果没有必要保留顺序,我们可以简单地将最后一个元素移到空白处。

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

答案 13 :(得分:0)

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

答案 14 :(得分:-1)

这是带有指针的游乐场示例。 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)
}

答案 15 :(得分:-2)

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