So löschen Sie ein Element aus einem Slice in Golang

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

Ich verwende diesen Befehl, um ein Element aus einem Slice zu löschen, aber es funktioniert nicht. Bitte schlagen Sie vor.

Anchal Sarraf
quelle
2
Dies ist eine gute Lektüre: blog.golang.org/go-slices-usage-and-internals
squiguy

Antworten:

160

Ordnung ist wichtig

Wenn Sie Ihr Array geordnet halten möchten, müssen Sie alle Elemente rechts vom Löschindex um eins nach links verschieben. Hoffentlich kann dies in Golang leicht gemacht werden:

func remove(slice []int, s int) []int {
    return append(slice[:s], slice[s+1:]...)
}

Dies ist jedoch ineffizient, da Sie möglicherweise alle Elemente verschieben müssen, was teuer ist.

Ordnung ist nicht wichtig

Wenn Sie sich nicht für die Bestellung interessieren, haben Sie die viel schnellere Möglichkeit, das zu löschende Element gegen das am Ende des Slice zu tauschen und dann die ersten n-1 Elemente zurückzugeben:

func remove(s []int, i int) []int {
    s[len(s)-1], s[i] = s[i], s[len(s)-1]
    return s[:len(s)-1]
}

Bei der Reslicing-Methode dauert das Entleeren eines Arrays von 1 000 000 Elementen 224 s, bei diesem dauert es nur 0,06 ns. Ich vermute, dass intern go nur die Länge des Slice ändert, ohne es zu ändern.

Bearbeiten 1

Schnelle Notizen basierend auf den Kommentaren unten (danke!).

Da der Zweck darin besteht, ein Element zu löschen, wird der zweite verschwendet, wenn die Reihenfolge keine Rolle spielt und ein einzelner Austausch erforderlich ist:

func remove(s []int, i int) []int {
    s[i] = s[len(s)-1]
    // We do not need to put s[i] at the end, as it will be discarded anyway
    return s[:len(s)-1]
}

Diese Antwort führt auch keine Grenzüberprüfung durch . Es erwartet einen gültigen Index als Eingabe. Dies bedeutet, dass negative Werte oder Indizes, die größer oder gleich len sind, zu Panik führen. Slices und Arrays sind 0-indiziert. Wenn Sie das n-te Element eines Arrays entfernen, müssen Sie die Eingabe n-1 bereitstellen . Um das erste Element zu entfernen, rufen Sie remove (s, 0) auf , um das zweite zu entfernen, rufen Sie remove (s, 1) auf und so weiter und so fort.

T. Claverie
quelle
19
Sieht so aus, als würden Sie nicht einmal einen Tausch brauchen, wenn Sie sich nicht darum kümmern, die ursprüngliche Scheibe zu erhalten, und das s[i] = s[len(s)-1]; return s[:len(s)-1]wäre genug.
Brad Peabody
@bgp Dies ist nur popping (das letzte Element zu entfernen) der Scheibe, nicht die unter dem Index in der ursprünglichen Frage vorgesehen Entfernen gibt. Ebenso können Sie verschieben (entfernen Sie das erste Element), mit return s[1:]dem auch die ursprüngliche Frage nicht beantwortet wird.
Shadyyx
2
Hm, nicht wirklich. Dies: s[i] = s[len(s)-1]Kopiert definitiv das letzte Element in das Element am Index i. Dann return s[:len(s)-1]kehrt die Scheibe ohne das letzte Element. Zwei Aussagen dort.
Brad Peabody
1
Schlägt mit len ​​(arr) == 2 fehl und das zu löschende Element ist das letzte: play.golang.org/p/WwD4PfUUjsM
zenocon
1
@zenocon In Golang sind Arrays 0-Index, was bedeutet, dass gültige Indizes für ein Array der Länge 2 0 und 1 sind. Tatsächlich überprüft diese Funktion nicht die Grenzen des Arrays und erwartet, dass ein gültiger Index bereitgestellt wird. Wenn len (arr) == 2 ist , sind gültige Argumente somit 0 oder 1 . Alles andere würde einen Zugriff außerhalb der Grenzen auslösen, und Go wird in Panik geraten.
T. Claverie
34

Entfernen Sie ein Element aus dem Slice (dies wird als "erneutes Schneiden" bezeichnet):

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]
}
wasmup
quelle
6
Es sei darauf hingewiesen, dass dies als "erneutes Schneiden" bezeichnet wird und relativ teuer ist, obwohl dies die idiomatische Methode ist, dies in Go zu tun. Verwechseln Sie dies nur nicht mit einer Operation wie dem Entfernen eines Knotens aus einer verknüpften Liste, da dies nicht der Fall ist. Wenn Sie dies häufig tun, insbesondere bei großen Sammlungen, sollten Sie alternative Designs in Betracht ziehen, die dies vermeiden.
evanmcdonnal
Ja, dies ist in Golang eine idiomatische Methode, und selbst in C / Assembly ist das Entfernen eines zufälligen Elements aus dem sortierten Array teuer. Sie müssen alle rechten Elemente um eine Position nach links verschieben (kopieren). Ja, in einigen Anwendungsfällen ist die verknüpfte Liste die bessere Lösung, um zufällige Elemente aus der Liste zu entfernen.
1
Beachten Sie, dass diese Methode bewirkt, dass alle geändert werden und nun n und alle auf einen Teil desselben zugrunde liegenden Arrays verweisen. Dies führt sehr wahrscheinlich zu Fehlern in Ihrem Code.
Mschuett
1
Diesen Fehler bekommen2019/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]:
OhhhThatVarun
10
Dies würde dazu führen, panicdass der Index das letzte Element im Slice
STEEL ist.
27

Kleiner Punkt (Code Golf), aber wenn die Reihenfolge keine Rolle spielt, müssen Sie die Werte nicht tauschen. Überschreiben Sie einfach die zu entfernende Array-Position mit einem Duplikat der letzten Position und geben Sie dann ein abgeschnittenes Array zurück.

func remove(s []int, i int) []int {
    s[i] = s[len(s)-1]
    return s[:len(s)-1]
}

Gleiches Ergebnis.

David
quelle
12
Die am besten lesbare Implementierung wäre, das erste Element in den angegebenen Index zu kopieren s[i] = s[0]und dann ein Array mit nur den letzten n-1 Elementen zurückzugeben. return s[1:]
Kent
3
Spielplatz von @Kent 's Lösung
Stevenspiel
2
@Kent das Problem mit Doing s[1:]versus s[:len(s)-1]ist, dass das spätere viel besser funktioniert, wenn das Slice später bearbeitet wird appendoder Löschungen mit appends vermischt werden . Letzteres behält die Slice-Kapazität bei, wohingegen Ersteres dies nicht tut.
Dave C
1
Wenn Sie das 0. Element mit dieser Funktion entfernen, wird das Ergebnis invertiert.
Ivarec
19

Das ist etwas seltsam zu sehen, aber die meisten Antworten hier sind gefährlich und beschönigen, was sie tatsächlich tun. Wenn Sie sich die ursprüngliche Frage ansehen, die zum Entfernen eines Elements aus dem Slice gestellt wurde, wird eine Kopie des Slice erstellt und dann gefüllt. Dies stellt sicher, dass beim Übergeben der Slices um Ihr Programm keine subtilen Fehler auftreten.

Hier ist ein Code, der die Antworten der Benutzer in diesem Thread mit dem ursprünglichen Beitrag vergleicht. Hier ist ein Spielplatz , auf dem Sie mit diesem Code herumspielen können.

Anhängebasierte Entfernung

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

Im obigen Beispiel können Sie sehen, wie ich ein Slice erstelle und es manuell mit den Nummern 0 bis 9 fülle. Wir entfernen dann Index 5 von allen und weisen ihn zu, um Index zu entfernen. Wenn wir jedoch jetzt alles ausdrucken, sehen wir, dass es ebenfalls geändert wurde. Dies liegt daran, dass Slices Zeiger auf ein zugrunde liegendes Array sind. Das Ausschreiben auf removeIndexUrsachen all, die ebenfalls geändert werden sollen, mit dem Unterschied, dass alles um ein Element länger ist, von dem aus es nicht mehr erreichbar ist removeIndex. Als nächstes ändern wir einen Wert in removeIndexund wir können sehen, dass er ebenfalls allgeändert wird. Effective go geht hier näher darauf ein.

Das folgende Beispiel werde ich nicht näher erläutern, aber es macht dasselbe für unsere Zwecke. Und zeigt nur, dass die Verwendung von Kopien nicht anders ist.

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

Die Fragen Originalantwort

Bei Betrachtung der ursprünglichen Frage wird das Slice, aus dem ein Element entfernt wird, nicht geändert. Die ursprüngliche Antwort in diesem Thread ist für die meisten Leute, die auf diese Seite kommen, die bisher beste.

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

Wie Sie sehen können, verhält sich diese Ausgabe so, wie es die meisten Leute erwarten würden und wahrscheinlich, was die meisten Leute wollen. Das Ändern von originalRemoveverursacht keine Änderungen in allund das Entfernen und Zuweisen des Index verursacht auch keine Änderungen! Fantastisch!

Dieser Code ist jedoch etwas langwierig, sodass der obige Code geändert werden kann.

Eine richtige Antwort

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

Fast identisch mit der ursprünglichen Lösung zum Entfernen von Indizes. Wir erstellen jedoch ein neues Slice, an das angehängt werden muss, bevor Sie zurückkehren.

mschuett
quelle
3
Dies sollte die Antwort auf die Frage sein, da dies die einzige ist, die das Risiko einer Änderung des Backing-Arrays des Slice erklärt
JessG,
Beim Anhängen wird das erste Slice allauch danach geändert, removeIndex := RemoveIndex(all, 5)da das Anhängen dasselbe zugrunde liegende Array wiederverwendet, wenn es über genügend Kapazität verfügt (das Entfernen eines Elements erfordert natürlich nicht mehr Kapazität). Wenn wir dagegen Elemente hinzufügen und zuweisen, removeIndexweist append ein neues Array zu und alländert sich nicht.
Schwarzgrün
9

Dies ist , wie Sie löschen Von einer Scheibe der golang Art und Weise . Sie müssen keine Funktion erstellen, die in den Anhang integriert ist. Probieren Sie es hier aus 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]
Kemar H.
quelle
8

Aus dem Buch The Go Programming Language

Um ein Element aus der Mitte eines Slice zu entfernen und dabei die Reihenfolge der verbleibenden Elemente beizubehalten, schieben Sie die höher nummerierten Elemente mit copy um eins nach unten, um die Lücke zu füllen:

func remove(slice []int, i int) []int {
  copy(slice[i:], slice[i+1:])
  return slice[:len(slice)-1]
}
Oleksandr Mosur
quelle
3
Beachten Sie, dass diese Methode bewirkt, dass das ursprünglich übergebene Slice geändert wird.
Mschuett
6

Ich gehe wie folgt vor, um das Element in Slice zu entfernen. Dies hilft bei der Lesbarkeit für andere. Und auch unveränderlich.

func remove(items []string, item string) []string {
    newitems := []string{}

    for _, i := range items {
        if i != item {
            newitems = append(newitems, i)
        }
    }

    return newitems
}
Madhan Ganesh
quelle
Ich mag diesen Ansatz besser, wenn Sie tatsächlich alle Vorkommen des Elements entfernen.
Eexit
1

Vielleicht können Sie diese Methode ausprobieren:

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

Verwendung:

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)

Ergebnis:

0, 1, 2, 4, 5
"0", "1", "2", "3", "5"
Karl Dönitz
quelle
1
Wahrscheinlich, weil es nicht idiomatisch und überentwickelt für das ist, was die Frage stellt. Es ist ein interessanter Weg, um es zu lösen, aber niemand sollte dies verwenden.
Mschuett
1
Vielen Dank! Eigentlich hat es für mich mit einem Interface ganz gut
geklappt
1

Vielleicht hilft dieser Code.

Es löscht ein Element mit einem bestimmten Index.

Nimmt das Array und den Index zum Löschen und gibt ein neues Array zurück, ähnlich der Append-Funktion.

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

Hier können Sie mit dem Code spielen: https://play.golang.org/p/aX1Qj40uTVs

Saad Hafa
quelle
1

Der beste Weg, dies zu tun, ist die Verwendung der Append-Funktion:

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

Der Name
quelle
0

Sie müssen nicht jedes einzelne Element überprüfen, es sei denn, Sie kümmern sich um den Inhalt und können Slice Append verwenden. Versuch es

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")
}
Los Los
quelle
-1

Hier ist das Spielplatzbeispiel mit Zeigern. 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)
}
user1858478
quelle