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])
}
我正在使用此命令从切片中删除一个元素,但它不起作用,请建议。
订单事项
如果要保持数组有序,则必须将删除索引右侧的所有元素向左移动一位。希望这可以在 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 方法,清空一个包含 1 000 000 个元素的数组需要 224 秒,而这个只需要 0.06 ns。
此答案不执行bounds-checking。它需要一个有效的索引作为输入。这意味着大于或等于初始 len(s)
的负值或索引将导致 Go 恐慌。
切片和数组索引为 0,删除数组的第 n 个元素意味着提供输入 n-1。要删除第一个元素,请调用 remove(s, 0),要删除第二个元素,请调用 remove(s, 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]
}
几乎与原始的删除索引解决方案相同,但是我们在返回之前创建了一个新的切片来追加。
append
如果不需要,它不会创建新的支持数组,那么为什么要删除创建一个新切片呢? go.dev/play/p/8V9rbjrDkt5
append
将在有空间时修改后备数组。从切片中删除是创建空间,因此任何涉及在删除时创建新切片的解决方案显然是错误的。
从 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]
all = RemoveIndex(all, 5)
fmt.Println(all) //[0 1 2 3 4 6 7 8 9]
}
2019/09/28 19:46:25 http: panic serving 192.168.1.3:52817: runtime error: slice bounds out of range [7:5] goroutine 7 [running]:
小点(代码高尔夫),但在顺序无关紧要的情况下,您不需要交换值。只需用最后一个位置的副本覆盖要删除的数组位置,然后返回一个截断的数组。
func remove(s []int, i int) []int {
s[i] = s[len(s)-1]
return s[:len(s)-1]
}
结果相同。
s[i] = s[0]
,然后返回一个仅包含最后 n-1 个元素的数组。 return s[1:]
s[1:]
相比 s[:len(s)-1]
的问题在于,如果稍后切片 append
或删除与 append
混合,则后者的性能会更好。后者保留了切片容量,而前者没有。
这就是您删除 切片 惯用方式的方式。您不需要构建一个内置在追加中的函数。在这里试试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]
来自书The Go Programming Language
要从切片中间删除一个元素,保留剩余元素的顺序,请使用 copy 将编号较高的元素向下滑动 1 以填充间隙: func remove(slice []int, i int) []int { 复制(slice[i:], slice[i+1:]) 返回 slice[:len(slice)-1] }
append
可以修改后备数组,为什么不能删除? go.dev/play/p/9eQStgiqYYK
s2 = append(s2, 2)
,如果您省略此步骤,后备数组 s2
将保持不变。与 go.dev/play/p/7EubNjwOFuD 比较
使用 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]。注意两点:
删除修改原始切片的内容
不过,您需要重新分配切片,否则它的长度会错误
append(slice[:s], slice[s+1:]...)
的包装函数。更多信息:cs.opensource.google/go/x/exp/+/0b5c67f0:slices/slices.go;l=156
我采用以下方法来删除切片中的项目。这有助于其他人的可读性。而且也是不可变的。
func remove(items []string, item string) []string {
newitems := []string{}
for _, i := range items {
if i != item {
newitems = append(newitems, i)
}
}
return newitems
}
最好的方法是使用 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
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]
}
找到一种方式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]
也许你可以试试这个方法:
// 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"
var value reflect.Value = reflect.ValueOf(array).Elem(); value.Set(reflect.AppendSlice(value.Slice(0, index), value.Slice(index+1, value.Len())))
。
使用泛型,您可以传递 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]
示例
https://go.dev/play/p/LhPGvEuZbRA
也许这段代码会有所帮助。
它删除具有给定索引的项目。
获取数组和要删除的索引并返回一个新数组,非常类似于 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
要从切片中间删除一个元素,保留剩余元素的顺序,请使用 copy 将编号较高的元素向下滑动 1 以填充间隙: func remove(slice []int, i int) []int { 复制(slice[i:], slice[i+1:]) 返回 slice[:len(slice)-1] }
如果不需要保留顺序,我们可以简单地将最后一个元素移动到间隙。 func remove(slice []int, i int) []int { slice[i] = slice[len(slice)-1] return slice[:len(slice)-1] }
在 language tutorial 中,我们了解到:
切片就像对数组的引用。切片不存储任何数据,它只描述底层数组的一部分。更改切片的元素会修改其底层数组的相应元素。
出于这个原因,在切片上使用 append
函数不关心我们正在处理的值的来源和目的地,对于 Go 哲学来说是非常危险和错误的。
因此,正确的解决方案是使用引用新数组而不是“主”数组的切片。这可以通过 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
}
或者更好的是,实现可以通过接口处理多种类型的功能。但是,所有这些都是一个很好的做法,因为您构建了一个函数来执行此操作,这与提出的问题无关。
但是,可以在 here 中找到 Go 操场上的测试示例。
除非您关心内容并且可以使用切片附加,否则无需检查每个元素。试试看
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")
}
删除
a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]
删除而不保留顺序
a[i] = a[len(a)-1]
a = a[:len(a)-1]
注意 如果元素的类型是指针或带有指针字段的结构体,需要进行垃圾回收,上述 Cut 和 Delete 的实现存在潜在的内存泄漏问题:一些有值的元素仍然被切片 a 引用,因此无法收集。下面的代码可以解决这个问题:
删除
copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]
删除而不保留顺序
a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]
你需要稍微改变你的代码,
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。
这是带有指针的操场示例。 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)
}
由于 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) }
...
s[i] = s[len(s)-1]
肯定会将最后一个元素复制到索引i
处的元素。然后,return s[:len(s)-1]
返回没有最后一个元素的切片。那里有两个陈述。s[len(s)-1], s[i] = 0, s[len(s)-1]
。如果您使用的是非原始数组,尤其如此。如果您有指向某些东西的指针,最好在切片之前制作要删除的元素nil
,这样您就没有基础数组中的指针。这个answer explains why very well。简而言之:在删除元素的位置移动最后一个元素后,将切片前的最后一个元素归零。