Я новичок в 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 ответов
Проблема в том, что при удалении элемента из исходного списка все последующие элементы сдвигаются . Но цикл range
не знает, что вы изменили базовый срез, и будет увеличивать индекс как обычно, хотя в этом случае этого не должно быть, потому что тогда вы пропускаете элемент.
И поскольку список 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]
Примечание.
Поскольку внешний цикл после внутреннего цикла ничего не содержит, вы можете заменить метку + continue простым 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.
Альтернатива
Альтернативой этому было бы движение внешнего цикла вниз, поэтому нет необходимости вручную уменьшать (или увеличивать) индексную переменную, потому что смещенные элементы не затронуты (уже обработаны из-за направления вниз).
В случае индекса:
//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
}
Комментарий можно удалить для увеличения производительности, когда срез не такой большой (время сортировки)
Вы можете использовать эти функции:
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
}
Вы должны быть осторожны при изменении среза во время итерации по нему.
Вот обычный способ удаления элементов из среза путем сжатия данных во время итерации по ним.
Он также использует карту, а не срез для исключенных элементов, что дает эффективность при большом количестве исключенных элементов.
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
.
Возможно, проще создать новый фрагмент, содержащий только желаемые элементы, например:
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)
}
Результат:
Have to ignore : test
Have to ignore : abc
[def ghi]
Похожие вопросы
Связанные вопросы
Новые вопросы
go
Go - это язык программирования с открытым исходным кодом. Он статически типизирован, с синтаксисом, свободно полученным из C, с добавлением автоматического управления памятью, безопасностью типов, некоторыми возможностями динамической типизации, дополнительными встроенными типами, такими как массивы переменной длины (называемые слайсами) и сопоставления ключ-значение, и большая стандартная библиотека.