Come eliminare un elemento da una fetta in Golang


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

Sto usando questo comando per eliminare un elemento da una fetta ma non funziona, suggerisci.


2

Risposte:


178

L'ordine conta

Se vuoi mantenere il tuo array ordinato, devi spostare tutti gli elementi a destra dell'indice di cancellazione di uno a sinistra. Si spera che questo possa essere fatto facilmente a Golang:

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

Tuttavia, questo è inefficiente perché potresti finire per spostare tutti gli elementi, il che è costoso.

L'ordine non è importante

Se non ti interessa ordinare, hai la possibilità molto più veloce di scambiare l'elemento da eliminare con quello alla fine dello slice e poi restituire i primi n-1 elementi:

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

Con il metodo reslicing, lo svuotamento di un array di 1 000 000 di elementi richiede 224 secondi, con questo solo 0,06 ns. Ho il sospetto che internamente, vai cambia solo la lunghezza della fetta, senza modificarla.

Modifica 1

Note rapide basate sui commenti qui sotto (grazie a loro!).

Poiché lo scopo è eliminare un elemento, quando l'ordine non ha importanza è necessario un singolo scambio, il secondo verrà sprecato:

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

Inoltre, questa risposta non esegue il controllo dei limiti . Si aspetta un indice valido come input. Ciò significa che valori negativi o indici maggiori o uguali a len (s) causeranno il panico di Go. Slice e array essendo indicizzati 0, la rimozione dell'elemento n-esimo di un array implica fornire input n-1 . Per rimuovere il primo elemento, chiama remove (s, 0) , per rimuovere il secondo, chiama remove (s, 1) e così via.


20
Sembra che se non ti interessa conservare la fetta originale, non avresti nemmeno bisogno di uno scambio e s[i] = s[len(s)-1]; return s[:len(s)-1]sarebbe sufficiente.
Brad Peabody

@bgp Questo è solo un popping (rimuovendo l'ultimo elemento) di slice, non rimuovendo quello sotto index fornito come nella domanda originale. Allo stesso modo potresti spostare (rimuovere il primo elemento) con il return s[1:]quale anche non risponde alla domanda originale.
shadyyx

2
Hm, non proprio. Questo: s[i] = s[len(s)-1]copia definitivamente l'ultimo elemento nell'elemento all'indice i. Quindi, return s[:len(s)-1]restituisce la sezione senza l'ultimo elemento. Due dichiarazioni lì.
Brad Peabody

1
Non riesce con len (arr) == 2 e l'elemento da eliminare è l'ultimo: play.golang.org/p/WwD4PfUUjsM
zenocon

1
@zenocon In Golang, gli array sono 0-index, il che significa che gli indici validi per un array di lunghezza 2 sono 0 e 1. In effetti, questa funzione non controlla i limiti dell'array e si aspetta che venga fornito un indice valido. Quando len (arr) == 2 , gli argomenti validi sono quindi 0 o 1 . Qualsiasi altra cosa attiverebbe un accesso fuori limite e Go andrà nel panico.
T. Claverie

41

Rimuovi un elemento dallo Slice (questo si chiama 're-slicing'):

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

8
Vale la pena sottolineare che questo è chiamato "re-slicing" ed è relativamente costoso sebbene sia il modo idiomatico di farlo in Go. Basta non confondere questo con un'operazione come la rimozione di un nodo da un elenco collegato perché non è così e se lo farai spesso, specialmente con raccolte di grandi dimensioni, dovresti considerare progetti alternativi che lo evitino.
evanmcdonnal

Sì, questo è un modo idiomatico in Golang, e anche in C / Assembly rimuovere un elemento casuale da un array ordinato è costoso, è necessario spostare (copiare) tutti gli elementi di destra di una posizione a sinistra. Sì, in alcuni casi d'uso l'elenco collegato è la soluzione migliore per rimuovere elementi casuali dall'elenco.

1
Si noti che questo metodo fa sì che tutto venga modificato e ora n e tutti puntano a una parte dello stesso array sottostante. È molto probabile che questo porti a bug nel codice.
mschuett

1
Ottenere questo errore2019/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
ciò causerebbe panicse l'indice fosse l'ultimo elemento della Slice
STEEL il

28

Punto minore (codice golf), ma nel caso in cui l'ordine non sia importante non è necessario scambiare i valori. Basta sovrascrivere la posizione dell'array rimossa con un duplicato dell'ultima posizione e quindi restituire un array troncato.

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

Stesso risultato.


13
L'implementazione più leggibile sarebbe copiare il primo elemento nell'indice specificato s[i] = s[0], quindi restituire un array con solo gli ultimi n-1 elementi. return s[1:]
Kent

4
parco giochi della soluzione di
@Kent

2
@Kent, il problema con il fare s[1:]contro s[:len(s)-1]è che il secondo funzionerà molto meglio se la fetta successivamente viene modificata appendo le eliminazioni sono mescolate con appends. Il secondo mantiene la capacità di slice dove, come il primo no.
Dave C

1
Se rimuovi l'elemento 0 con questa funzione, il risultato viene invertito.
ivarec

21

Questo è un po 'strano da vedere, ma la maggior parte delle risposte qui sono pericolose e ignorano ciò che stanno effettivamente facendo. Guardando la domanda originale che è stata posta sulla rimozione di un elemento dalla sezione, viene creata una copia della sezione e quindi viene riempita. Ciò garantisce che mentre le sezioni vengono passate nel programma non si introducano piccoli bug.

Ecco del codice che confronta le risposte degli utenti in questo thread e nel post originale. Ecco un go playground per scherzare con questo codice.

Aggiungi rimozione basata

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

Nell'esempio sopra puoi vedermi creare una sezione e riempirla manualmente con i numeri da 0 a 9. Quindi rimuoviamo l'indice 5 da tutto e lo assegniamo per rimuovere l'indice. Tuttavia quando andiamo a stampare tutto ora vediamo che è stato modificato anche lui. Questo perché le slice sono puntatori a un array sottostante. Scrivendolo anche per removeIndexcause allda modificare con la differenza che allè più lungo di un elemento da cui non è più raggiungibile removeIndex. Successivamente modifichiamo un valore in removeIndexe possiamo vedere che allviene modificato anche. Vai efficace entra in qualche dettaglio in più su questo.

Non entrerò nel seguente esempio, ma fa la stessa cosa per i nostri scopi. E dimostra solo che l'uso della copia non è diverso.

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

La risposta originale alle domande

Guardando la domanda originale non modifica la sezione da cui rimuove un elemento. Rendere la risposta originale in questo thread la migliore finora per la maggior parte delle persone che visitano questa pagina.

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

Come puoi vedere, questo output agisce come la maggior parte delle persone si aspetterebbe e probabilmente ciò che la maggior parte delle persone desidera. La modifica di originalRemovenon causa cambiamenti alle anche l'operazione di rimozione dell'indice e assegnazione non causa cambiamenti! Fantastico!

Questo codice è un po 'lungo, quindi quanto sopra può essere modificato in questo.

Una risposta corretta

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

Quasi identica alla soluzione di rimozione dell'indice originale, tuttavia creiamo una nuova fetta da aggiungere prima di tornare.


3
questa dovrebbe essere la risposta alla domanda poiché è l'unica che spiega il rischio di modificare l'array di supporto dello slice
JessG

Nella rimozione basata sull'append, la prima slice allviene modificata anche dopo removeIndex := RemoveIndex(all, 5)perché append riutilizza lo stesso array sottostante se ha capacità sufficiente (la rimozione di un elemento ovviamente non richiede più capacità). Al contrario, se aggiungiamo elementi e assegniamo a removeIndex, append allocherà un nuovo array e allnon cambierà.
blackgreen il

11

Questo è il modo per Elimina Da una fetta del modo golang . Non è necessario creare una funzione che è incorporata nell'append. Provalo qui 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]

8

Dal libro The Go Programming Language

Per rimuovere un elemento dal centro di una sezione, preservando l'ordine degli elementi rimanenti, usa copia per far scorrere gli elementi con il numero più alto verso il basso di uno per riempire il vuoto:

func remove(slice []int, i int) []int {
  copy(slice[i:], slice[i+1:])
  return slice[:len(slice)-1]
}

3
Notare che questo metodo causa la modifica della slice originale passata.
mschuett

7

Adotto l'approccio seguente per rimuovere l'elemento in slice. Questo aiuta nella leggibilità per gli altri. E anche immutabile.

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

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

    return newitems
}

Mi piace di più questo approccio, in realtà stai rimuovendo tutte le occorrenze dell'elemento.
eexit

Questo può essere facilmente trasformato filtrando diversi elementi dalla fetta, bello!
prelievo eldad il

1

Forse puoi provare questo metodo:

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

Utilizzo:

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)

Risultato:

0, 1, 2, 4, 5
"0", "1", "2", "3", "5"

1
Probabilmente perché non è idiomatico e troppo ingegnerizzato per ciò che la domanda sta chiedendo. È un modo interessante per risolverlo, ma nessuno dovrebbe usarlo.
mschuett

1
Grazie! In realtà ha funzionato bene per me con un'interfaccia, dal momento che sembra essere universale, forse
DADi590

1

Forse questo codice aiuterà.

Elimina l'elemento con un determinato indice.

Prende l'array e l'indice da eliminare e restituisce un nuovo array molto simile alla funzione 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]
}

Qui puoi giocare con il codice: https://play.golang.org/p/aX1Qj40uTVs


1

il modo migliore per farlo è usare la funzione 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


1

Trova un modo qui senza trasferirti.

  • cambia l'ordine
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]
  • tenere in ordine
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]

0

Non è necessario controllare ogni singolo elemento a meno che non ti interessi i contenuti e non puoi utilizzare slice append. Provalo

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

-1

ecco l'esempio del playground con dei puntatori. 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)
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.