Удалить элемент в срезе

140
func main() {
    a := []string{"Hello1", "Hello2", "Hello3"}
    fmt.Println(a)
    // [Hello1 Hello2 Hello3]
    a = append(a[:0], a[1:]...)
    fmt.Println(a)
    // [Hello2 Hello3]
}

Как работает этот трюк с удалением с функцией добавления?

Казалось бы, он захватывает все до первого элемента (пустой массив)

Затем добавляем все после первого элемента (нулевая позиция)

Что делает ... (точка, точка)?

Хорхе Оливеро
источник
1
Проверьте code.google.com/p/go-wiki/wiki/SliceTricks
OneOfOne,
3
Почему бы не взглянуть на спецификацию языка? ...там подробно объясняется?
Volker
9
С одной стороны, абсолютно верно ( golang.org/ref/spec , все); с другой стороны, эти идиомы достаточно незнакомы для миграции Pythonistas и т. д., и я не возражаю против того, чтобы другие могли найти объяснение.
twotwotwo
См. Github.com/golang/go/wiki/SliceTricks
Евгений Ткаченко

Ответы:

279

Где aнаходится срез, а iэто индекс элемента, который вы хотите удалить:

a = append(a[:i], a[i+1:]...)

... - синтаксис для вариативных аргументов в Go.

По сути, при определении функции она помещает все аргументы, которые вы передаете, в один фрагмент этого типа. Поступая так, вы можете передавать столько аргументов, сколько хотите (например, fmt.Printlnможете принимать столько аргументов, сколько хотите).

Теперь при вызове функции ...происходит обратное: он распаковывает срез и передает их как отдельные аргументы в вариационную функцию.

Итак, что делает эта строка:

a = append(a[:0], a[1:]...)

по сути:

a = append(a[:0], a[1], a[2])

Теперь вам может быть интересно, почему бы просто не сделать

a = append(a[1:]...)

Ну, функция определения appendявляется

func append(slice []Type, elems ...Type) []Type

Таким образом, первый аргумент должен быть срезом правильного типа, второй аргумент - вариативным, поэтому мы передаем пустой срез, а затем распаковываем остальную часть среза, чтобы заполнить аргументы.

Дэйв
источник
35
Разве вы не выходите за пределы диапазона исключения, если i - последний элемент среза? a = append(a[:i], a[i+1:]...)
themihai
5
@DaveC Я получаю эту ошибку при работе со своими фрагментами в моем проекте: /
Tyguy7
3
@ Tyguy7 из спецификации: «Для массивов или строк индексы находятся в диапазоне, если 0 <= low <= high <= len (a), в противном случае они находятся вне диапазона». Может быть, в вашем случае высокий <низкий; в этом случае вы получите ошибку. ( golang.org/ref/spec#Slice_expressions )
mlg
2
Как это работает? Я серьезно надеюсь, что это не создаст совершенно новый кусок под капотом ..
joonas.fi
7
@ Tyguy7 Я думаю, вы пытались удалить элементы среза внутри цикла. Так что с индексами нужно быть осторожным.
Николай Быстрицкий
43

Есть два варианта:

A: Вы заботитесь о сохранении порядка в массиве:

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

B: Вы не заботитесь о сохранении порядка (это, вероятно, быстрее):

a[i] = a[len(a)-1] // Replace it with the last one. CAREFUL only works if you have enough elements.
a = a[:len(a)-1]   // Chop off the last one.

См. Ссылку, чтобы увидеть последствия утечки памяти, если ваш массив состоит из указателей.

https://github.com/golang/go/wiki/SliceTricks

Крис
источник
Это интересно, но не совсем отвечает на вопрос
Брайан
Это хорошо, но было бы лучше, если бы он мог удалить единственный элемент в массиве
Naguib Ihab
2
Просто предупреждаю, попытка использовать первый элемент из b (заменить на последний элемент), очевидно, не работает, если вы пытаетесь удалить последний элемент в срезе lol
Sirens
13

Вместо того, чтобы рассматривать индексы в [a:]-, [:b]- и [a:b]-notations как индексы элементов, думайте о них как о индексах промежутков вокруг и между элементами, начиная с промежутка, индексированного 0перед элементом, индексированным как 0.

введите описание изображения здесь

Глядя только на синие числа, гораздо легче понять, что происходит: [0:3]все окружает, [3:3]пусто и [1:2]уступит {"B"}. Тогда [a:]это всего лишь короткая версия [a:len(arrayOrSlice)], [:b]короткая версия [0:b]и [:]короткая версия [0:len(arrayOrSlice)]. Последний обычно используется для превращения массива в срез, когда это необходимо.

Зил
источник
1
Это помогает объяснить , почему ответ «нет» , чтобы themihai игровая комментарий на Дэйва ответ , ссылаясь на [я + 1:] , даже когда речь идет о го элемента: play.golang.org/p/E0lQ3jPcjX5
Ник P
5

... это синтаксис для вариативных аргументов.

Я думаю, что это реализовано компилятором с помощью slice ( []Type), как и функция append:

func append(slice []Type, elems ...Type) []Type

когда вы используете "elems" в "append", на самом деле это срез (тип []). Итак, " a = append(a[:0], a[1:]...)" означает " a = append(a[0:0], a[1:])"

a[0:0] это кусок, в котором ничего нет

a[1:] это "Hello2 Hello3"

Вот как это работает

Франклин
источник
2
a[0:0]это не nilкусок с нулевой длиной. a[0:0]будет только быть , nilесли aесть nil.
icza
5

Я получаю сообщение об ошибке индекса вне допустимого диапазона с принятым ответом. Причина: когда начинается диапазон, это не итерация значений по одному, а итерация по индексу. Если вы изменили срез, пока он находится в пределах досягаемости, это вызовет некоторые проблемы.

Старый ответ:

chars := []string{"a", "a", "b"}

for i, v := range chars {
    fmt.Printf("%+v, %d, %s\n", chars, i, v)
    if v == "a" {
        chars = append(chars[:i], chars[i+1:]...)
    }
}
fmt.Printf("%+v", chars)

Ожидается:

[a a b], 0, a
[a b], 0, a
[b], 0, b
Result: [b]

Актуально:

// Autual
[a a b], 0, a
[a b], 1, b
[a b], 2, b
Result: [a b]

Правильный способ (решение):

chars := []string{"a", "a", "b"}

for i := 0; i < len(chars); i++ {
    if chars[i] == "a" {
        chars = append(chars[:i], chars[i+1:]...)
        i-- // form the remove item index to start iterate next item
    }
}

fmt.Printf("%+v", chars)

Источник: https://dinolai.com/notes/golang/golang-delete-slice-item-in-range-problem.html

Тимотью
источник
3

В вики golang показаны некоторые приемы для среза, включая удаление элемента из среза.

Ссылка: введите описание ссылки здесь

Например, a - это фрагмент, который вы хотите удалить элементом i.

a = append(a[:i], a[i+1:]...)

ИЛИ

a = a[:i+copy(a[i:], a[i+1:])]
g10guang
источник