删除切片中的元素

时间:2015-02-24 15:17:24

标签: go slice

我是Golang的新手,我试图根据另一个切片中的元素删除一个切片中的元素。 例如

输入切片:urlList := []string{"test", "abc", "def", "ghi"}

要删除切片的元素:remove := []string{"abc", "test"}

预期输出切片:urlList := []string{"def", "ghi"}

这就是我的尝试。

func main() {

    urlList := []string{"test", "abc", "def", "ghi"}
    remove := []string{"abc", "test"}
loop:
    for i, url := range urlList {
        for _, rem := range remove {
            if url == rem {
                urlList = append(urlList[:i], urlList[i+1:]...)
                continue loop
            }
        }
    }
    for _, v := range urlList {
        fmt.Println(v)
    }
}

但它没有像我预期的那样工作。我不知道自己错过了什么。

5 个答案:

答案 0 :(得分:7)

问题在于,当您从原始列表中删除元素时,所有后续元素都将移位。但是range循环并不知道你改变了底层切片并且会像往常一样递增索引,即使在这种情况下它也不应该因为你跳过一个元素。 / p>

由于remove列表包含原始列表中紧挨着的2个元素,因此第二个元素(在这种情况下为"abc")将不会检查,不会被删除。

一种可能的解决方案是不在外部循环中使用range,当您删除元素时,手动减少索引 i-- ,因为继续下一次迭代它会自动增加:

urlList := []string{"test", "abc", "def", "ghi"}
remove := []string{"abc", "test"}

loop:
for i := 0; i < len(urlList); i++ {
    url := urlList[i]
    for _, rem := range remove {
        if url == rem {
            urlList = append(urlList[:i], urlList[i+1:]...)
            i-- // Important: decrease index
            continue loop
        }
    }
}

fmt.Println(urlList)

<强>输出:

[def ghi]

注意:

由于外部循环在内部循环后不包含任何内容,因此您可以使用简单的break替换标签+ continue:

urlList := []string{"test", "abc", "def", "ghi"}
remove := []string{"abc", "test"}

for i := 0; i < len(urlList); i++ {
    url := urlList[i]
    for _, rem := range remove {
        if url == rem {
            urlList = append(urlList[:i], urlList[i+1:]...)
            i-- // Important: decrease index
            break
        }
    }
}

fmt.Println(urlList)

Go Playground上试用。

<强>替代

另一种方法是外循环向下,因此不需要手动减少(或增加)索引变量,因为移位的元素不受影响(由于向下方向已经处理)。

答案 1 :(得分:2)

创建一个只包含所需元素的新切片可能更简单,例如:

package main

import "fmt"

func main() {
    urlList := []string{"test", "abc", "def", "ghi"}
    remove := []string{"abc", "test"}

    new_list := make([]string, 0)

    my_map := make(map[string]bool, 0)
    for _, ele := range remove {
        my_map[ele] = true
    }

    for _, ele := range urlList {
        _, is_in_map := my_map[ele]
        if is_in_map {
            fmt.Printf("Have to ignore : %s\n", ele)
        } else {
            new_list = append(new_list, ele)    
        }
    }

    fmt.Println(new_list)

}

playground

结果:

Have to ignore : test
Have to ignore : abc
[def ghi]

答案 2 :(得分:1)

在迭代时修改切片时必须小心。

这是通过在迭代数据的同时压缩数据来从切片中删除元素的常用方法。

对于排除的元素,它还使用映射而不是切片,这可以在排除项的数量很大时提供效率。

Exclude更新了xs,这就是使用指针参数的原因。另一种方法是更新xs的后备数组,但是以与内置append相同的方式返回函数中的切片。

package main

import "fmt"

func Exclude(xs *[]string, excluded map[string]bool) {
    w := 0
    for _, x := range *xs {
        if !excluded[x] {
            (*xs)[w] = x
            w++
        }
    }
    *xs = (*xs)[:w]
}

func mapFromSlice(ex []string) map[string]bool {
    r := map[string]bool{}
    for _, e := range ex {
        r[e] = true
    }
    return r
}

func main() {
    urls := []string{"test", "abc", "def", "ghi"}
    remove := mapFromSlice([]string{"abc", "test"})
    Exclude(&urls, remove)
    fmt.Println(urls)
}

此代码在运行时为O(N + M),其中N是urls的长度,M是remove的长度。

答案 3 :(得分:0)

您可以使用以下功能:

func main() {
  array := []string{"A", "B", "C", "D", "E"}
  a = StringSliceDelete(a, 2) // delete "C"
  fmt.Println(a) // print: [A, B, D E]
}

//IntSliceDelete function
func IntSliceDelete(slice []int, index int) []int {
   copy(slice[index:], slice[index+1:])
   new := slice[:len(slice)-1]
   return new
}

//StringSliceDelete function
func StringSliceDelete(slice []string, index int) []string {
    copy(slice[index:], slice[index+1:])
    new := slice[:len(slice)-1]
    return new
}

// ObjectSliceDelete function
func ObjectSliceDelete(slice []interface{}, index int) []interface{} {
    copy(slice[index:], slice[index+1:])
    new := slice[:len(slice)-1]
    return new
}

答案 4 :(得分:0)

如果是索引:

//RemoveElements delete the element of the indexes contained in j of the data in input
func RemoveElements(data []string, j []int) []string {
    var newArray []string
    var toAdd bool = true
    var removed int = 0
    //sort.Ints(j)
    for i := 0; i < len(data); i++ {
        for _, k := range j {
            // if k < i || k > i {
            //  break
            // } else
            if i == k {
                toAdd = false
                break
            }
        }
        if toAdd {
            newArray = append(newArray, data[i])
            removed++
        }
        toAdd = true
    }
    return newArray
}

当切片不是很大(排序时间)时,可以删除注释以提高性能