删除切片中的元素

huangapple go评论73阅读模式
英文:

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

但它没有按照我期望的工作。我不知道我漏掉了什么。

英文:

I am complete newbie in Golang, I am trying to remove elements in one slice based on the elements in another slice.
e.g.

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

elements to remove slice : remove := []string{"abc", "test"}

expected output slice : urlList := []string{"def", "ghi"}

This is what I tried.

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

But it's not working as I expected. I don't know what I am missing.

答案1

得分: 9

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

由于remove列表包含两个相邻的元素,它们在原始列表中,第二个元素(在这种情况下是“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-- // 重要:减少索引
			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-- // 重要:减少索引
			break
		}
	}
}

fmt.Println(urlList)

Go Playground上尝试一下。

替代方案

另一种替代方案是外部循环向下进行,因此不需要手动增加或减少索引变量,因为已经处理了移动的元素(由于向下方向)。

英文:

The problem is that when you remove an element from the original list, all subsequent elements are shifted. But the range loop doesn't know that you changed the underlying slice and will increment the index as usual, even though in this case it shouldn't because then you skip an element.

And since the remove list contains 2 elements which are right next to each other in the original list, the second one (&quot;abc&quot; in this case) will not be checked and will not be removed.

One possible solution is not to use range in the outer loop, and when you remove an element, you manually decrease the index i-- because continuing with the next iteration it will get auto-incremented:

urlList := []string{&quot;test&quot;, &quot;abc&quot;, &quot;def&quot;, &quot;ghi&quot;}
remove := []string{&quot;abc&quot;, &quot;test&quot;}

loop:
for i := 0; i &lt; 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)

Output:

[def ghi]

Note:

Since the outer loop contains nothing after the inner loop, you can replace the label+continue with a simple break:

urlList := []string{&quot;test&quot;, &quot;abc&quot;, &quot;def&quot;, &quot;ghi&quot;}
remove := []string{&quot;abc&quot;, &quot;test&quot;}

for i := 0; i &lt; 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)

Try it on Go Playground.

Alternative

An alternative to this would be for the outer loop to go downward, so no need to manually decrease (or increase) the index variable because the shifted elements are not affected (already processed due to the downward direction).

答案2

得分: 3

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

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("需要忽略的元素:%s\n", ele)
        } else {
            new_list = append(new_list, ele)
        }
    }

    fmt.Println(new_list)

}

结果:

需要忽略的元素:test
需要忽略的元素:abc
[def ghi]

playground

英文:

Maybe it is simpler to create a new slice, that only contains your desired elements, for example:

package main

import &quot;fmt&quot;

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

	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(&quot;Have to ignore : %s\n&quot;, ele)
		} else {
			new_list = append(new_list, ele)	
		}
	}

	fmt.Println(new_list)
	
}

playground

Result:

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

答案3

得分: 1

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

下面是一种常见的方法,可以在迭代切片时同时压缩数据来删除元素。

它还使用了一个映射(map)而不是切片来存储被排除的元素,这在排除项的数量很大时可以提高效率。

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 的长度。

英文:

You have to be careful when modifying a slice while iterating over it.

Here's a common way to remove elements from a slice by compacting the data at the same time as iterating over it.

It also uses a map rather than a slice for excluded elements, which gives efficiency when the number of excluded items is large.

Exclude updates xs in place, which is why a pointer argument is used. An alternative would be to update the backing array of xs, but to return the slice from the function in the same way that the built-in append works.

package main

import &quot;fmt&quot;

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{&quot;test&quot;, &quot;abc&quot;, &quot;def&quot;, &quot;ghi&quot;}
	remove := mapFromSlice([]string{&quot;abc&quot;, &quot;test&quot;})
	Exclude(&amp;urls, remove)
	fmt.Println(urls)
}

This code is O(N+M) in run-time where N is the length of urls and M is the length of remove.

答案4

得分: 0

你可以使用以下函数:

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

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

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

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

以上是要翻译的内容。

英文:

You can use these functions:

func main() {
  array := []string{&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;}
  a = StringSliceDelete(a, 2) // delete &quot;C&quot;
  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
}

答案5

得分: 0

在索引情况下:

// RemoveElements函数删除输入数据中包含在j中的索引的元素
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
}

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

英文:

In case of index:

//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 &lt; len(data); i++ {
		for _, k := range j {
			// if k &lt; i || k &gt; i {
			// 	break
			// } else
			if i == k {
				toAdd = false
				break
			}
		}
		if toAdd {
			newArray = append(newArray, data[i])
			removed++
		}
		toAdd = true
	}
	return newArray
}

Comment can be removed for increase performance when the slice is not so big (sort time)

答案6

得分: 0

尝试一下

https://go.dev/play/p/CKvGWl7vG4_V

elements := []string{"one", "two", "three", "four"}
needToRemove := []string{"one", "four"}

newSlice := remove(elements, needToRemove) // []string{"two", "three"}


func remove(slice, elements []string) []string {
    out := []string{}
    bucket := map[string]bool{}

    for _, element := range slice {
        if !inSlice(elements, element) && !bucket[element] {
            out = append(out, element)
            bucket[element] = true
        }
    }

    return out
}

func inSlice(slice []string, elem string) bool {
    for _, i := range slice {
        if i == elem {
            return true
        }
    }

    return false
}
英文:

Try it

https://go.dev/play/p/CKvGWl7vG4_V

elements := []string{&quot;one&quot;, &quot;two&quot;, &quot;three&quot;, &quot;four&quot;}
needToRemove := []string{&quot;one&quot;, &quot;four&quot;}

newSlice := remove(elements, needToRemove) // []string{&quot;two&quot;, &quot;three&quot;}


func remove(slice, elements []string) []string {
    out := []string{}
    bucket := map[string]bool{}

    for _, element := range slice {
        if !inSlice(elements, element) &amp;&amp; !bucket[element] {
            out = append(out, element)
            bucket[element] = true
        }
    }

    return out
}

func inSlice(slice []string, elem string) bool {
    for _, i := range slice {
        if i == elem {
            return true
        }
    }

    return false
}

huangapple
  • 本文由 发表于 2015年2月24日 23:17:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/28699485.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定