Element in einem Slice löschen

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

Wie funktioniert dieser Löschtrick mit der Append-Funktion?

Es scheint, dass es alles vor dem ersten Element (leeres Array) erfasst.

Dann alles nach dem ersten Element anhängen (Position Null)

Was macht der ... (Punkt Punkt Punkt)?

Jorge Olivero
quelle
1
Überprüfen Sie code.google.com/p/go-wiki/wiki/SliceTricks
OneOfOne
3
Schauen Sie sich doch einmal die Sprachspezifikation an. ...wird dort ausführlich erklärt?
Volker
9
Einerseits absolut wahr ( golang.org/ref/spec , alle); Auf der anderen Seite gibt es genug Unbekanntes an diesen Redewendungen für die Migration von Pythonisten usw., so dass es mir nichts ausmacht, eine Erklärung zu haben, die andere finden können.
Zweiundzwanzig
Siehe github.com/golang/go/wiki/SliceTricks
Evgeniy Tkachenko

Antworten:

276

Wo aist das Slice und ider Index des Elements, das Sie löschen möchten:

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

... ist die Syntax für verschiedene Argumente in Go.

Grundsätzlich werden beim Definieren einer Funktion alle Argumente, die Sie übergeben, in einem Slice dieses Typs zusammengefasst. Auf diese Weise können Sie so viele Argumente übergeben, wie Sie möchten (z. B. fmt.Printlnkönnen Sie so viele Argumente verwenden, wie Sie möchten).

Wenn Sie eine Funktion aufrufen , ...geschieht das Gegenteil: Sie entpackt ein Slice und übergibt es als separate Argumente an eine variable Funktion.

Also, was diese Zeile macht:

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

Ist im Wesentlichen:

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

Nun fragen Sie sich vielleicht, warum nicht einfach

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

Nun, die Funktionsdefinition von appendist

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

Das erste Argument muss also ein Slice des richtigen Typs sein, das zweite Argument ist das Variadic. Wir übergeben also ein leeres Slice und entpacken dann den Rest des Slice, um die Argumente auszufüllen.

Dave
quelle
35
Erhalten Sie keine Ausnahme außerhalb des Bereichs, wenn ich das letzte Element aus Slice bin? a = append(a[:i], a[i+1:]...)
Themihai
5
@ DaveC Ich bekomme diesen Fehler, wenn ich mit meinen Slices in meinem Projekt
arbeite
3
@ Tyguy7 aus der Spezifikation: "Bei Arrays oder Strings liegen die Indizes im Bereich, wenn 0 <= niedrig <= hoch <= len (a), andernfalls liegen sie außerhalb des Bereichs." Vielleicht in Ihrem Fall hoch <niedrig; In diesem Fall erhalten Sie den Fehler. ( golang.org/ref/spec#Slice_expressions )
mlg
2
Wie ist die Leistung davon? Ich hoffe ernsthaft, dass es kein völlig neues Stück unter der Haube
schafft
7
@ Tyguy7 Ich denke du hast versucht Elemente von Slice innerhalb einer Schleife zu löschen. Sie müssen also vorsichtig mit Indizes sein.
Nikolay Bystritskiy
42

Es gibt zwei Möglichkeiten:

A: Sie möchten die Array-Reihenfolge beibehalten:

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

B: Es ist Ihnen egal, ob Sie die Ordnung aufrechterhalten (dies ist wahrscheinlich schneller):

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.

Unter dem Link erfahren Sie, welche Auswirkungen Speicherlecks haben, wenn Ihr Array Zeiger enthält.

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

Chris
quelle
Dies ist interessant, aber nicht wirklich die Frage zu beantworten
Bryan
Das ist schön, aber es wäre besser gewesen, wenn es das einzige Element im Array löschen könnte
Naguib Ihab
2
Nur ein Kopf hoch, der Versuch, das erste von b zu verwenden (durch letztes Element ersetzen), funktioniert offensichtlich nicht, wenn Sie versuchen, das letzte Element im Slice zu entfernen lol
Sirens
13

Stellen Sie sich die Indizes in den [a:]-, [:b]- und [a:b]-Notationen nicht als Elementindizes vor, sondern als Indizes der Lücken um und zwischen den Elementen, beginnend mit der Lücke, die 0vor dem als indizierten Element indiziert wurde 0.

Geben Sie hier die Bildbeschreibung ein

Wenn man nur die blauen Zahlen betrachtet, ist es viel einfacher zu sehen, was los ist: [0:3]schließt alles ein, [3:3]ist leer und [1:2]würde nachgeben {"B"}. Dann [a:]ist nur die Kurzversion von [a:len(arrayOrSlice)], [:b]die Kurzversion von [0:b]und [:]die Kurzversion von [0:len(arrayOrSlice)]. Letzteres wird üblicherweise verwendet, um ein Array bei Bedarf in ein Slice umzuwandeln.

Zyl
quelle
1
Dies hilft zu erklären , warum die Antwort „nein“ zu themihai der ist Kommentar auf Daves Antwort auf Bezug [i + 1:] auch bei Bezugnahme auf das i - te Element: play.golang.org/p/E0lQ3jPcjX5
Nick P
5

... ist die Syntax für verschiedene Argumente.

Ich denke, es wird vom Complier mithilfe von Slice implementiert ( []Type)genau wie die Funktion append:

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

Wenn Sie "elems" in "append" verwenden, handelt es sich tatsächlich um einen Slice-Typ ([]). " a = append(a[:0], a[1:]...)" Bedeutet also " a = append(a[0:0], a[1:])"

a[0:0] ist eine Scheibe, die nichts hat

a[1:] ist "Hello2 Hello3"

So funktioniert es

Franklin
quelle
2
a[0:0]ist nicht nilnur ein Slice mit 0 Länge. a[0:0]wird nur sein, nilwenn aist nil.
icza
5

Ich erhalte einen Indexfehler außerhalb des Bereichs mit der akzeptierten Antwortlösung. Grund: Wenn der Bereich beginnt, wird der Wert nicht einzeln iteriert, sondern nach Index iteriert. Wenn Sie ein Slice geändert haben, während es sich in Reichweite befindet, tritt ein Problem auf.

Alte Antwort:

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)

Erwartet :

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

Tatsächlich:

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

Richtiger Weg (Lösung):

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)

Quelle: https://dinolai.com/notes/golang/golang-delete-slice-item-in-range-problem.html

timotew
quelle
3

In Golangs Wiki werden einige Tricks für Slice gezeigt, darunter das Löschen eines Elements aus Slice.

Link: Geben Sie hier die Linkbeschreibung ein

Zum Beispiel ist a das Slice, in dem Sie das Element Nummer i löschen möchten.

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

ODER

a = a[:i+copy(a[i:], a[i+1:])]
g10guang
quelle