删除切片中的元素

Remove elements in 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)
    }
}

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

问题是,当您从原始列表中删除一个元素时,所有后续元素都会移位。但是 range 循环不知道您更改了底层切片并将像往常一样增加索引,即使在这种情况下它不应该因为那样您 skip元素.

并且由于 remove 列表包含 2 个元素,它们在原始列表中 彼此相邻 ,因此第二个元素("abc" 在这个case) 不会被检查,也不会被删除。

一个可能的解决方案是不在外循环中使用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:

替换标签+继续
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 上试用。

备选

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

也许创建一个新切片更简单,它只包含您想要的元素,例如:

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]

在遍历切片时修改切片时必须小心。

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

它还使用 map 而不是 slice 来排除元素,这在排除项数量很大时提供了效率。

Exclude 就地更新 xs,这就是使用指针参数的原因。另一种方法是更新 xs 的后备数组,但更新 return 函数中的切片,其方式与内置 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 的长度。

您可以使用这些功能:

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
}

索引情况下:

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

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