Eliminar elemento en un segmento

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

¿Cómo funciona este truco de eliminación con la función append?

Parece que está agarrando todo antes del primer elemento (matriz vacía)

Luego, agregue todo después del primer elemento (posición cero)

¿Qué hace el ... (dot dot dot)?

Jorge Olivero
fuente
3
¿Por qué no echar un vistazo a las especificaciones del idioma? ...se explica allí en detalle?
Volker
9
Por un lado, absolutamente cierto ( golang.org/ref/spec , todos); Por otro lado, no hay suficiente familiaridad con estos modismos para migrar Pythonistas, etc., que no me importa tener una explicación para que otros la encuentren.
twotwotwo

Respuestas:

276

Dónde aestá el segmento y ies el índice del elemento que desea eliminar:

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

... es la sintaxis para argumentos variados en Go.

Básicamente, al definir una función, coloca todos los argumentos que pasa en una porción de ese tipo. Al hacerlo, puede pasar tantos argumentos como desee (por ejemplo, fmt.Printlnpuede tomar tantos argumentos como desee).

Ahora, cuando se llama a una función, ...hace lo contrario: desempaqueta un segmento y los pasa como argumentos separados a una función variable.

Entonces, qué hace esta línea:

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

Es esencial:

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

Ahora, te estarás preguntando, ¿por qué no solo hacer

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

Bueno, la definición de la función appendes

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

Entonces, el primer argumento tiene que ser un segmento del tipo correcto, el segundo argumento es el variadic, por lo que pasamos un segmento vacío y luego desempaquetamos el resto del segmento para completar los argumentos.

Dave
fuente
35
¿No sale de la excepción de rango si yo es el último elemento de la rebanada? a = append(a[:i], a[i+1:]...)
themihai
55
@DaveC Recibo ese error cuando trabajo con mis sectores en mi proyecto: /
Tyguy7
3
@ Tyguy7 de la especificación: "Para matrices o cadenas, los índices están en el rango si 0 <= bajo <= alto <= len (a), de lo contrario están fuera del rango". Quizás en su caso alto <bajo; en ese caso obtendrás el error. ( golang.org/ref/spec#Slice_expressions )
mlg
2
¿Cómo es el desempeño de esto? Espero seriamente que no esté creando un corte completamente nuevo debajo del capó ..
joonas.fi
77
@ Tyguy7 Creo que intentaste eliminar elementos de corte dentro de un bucle. Entonces debes tener cuidado con los índices.
Nikolay Bystritskiy
42

Hay dos opciones:

A: le importa retener el orden de la matriz:

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

B: No te importa retener el orden (probablemente esto sea más rápido):

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.

Vea el enlace para ver las implicaciones de las pérdidas de memoria si su matriz es de punteros.

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

Chris
fuente
Esto es interesante, pero realmente no responde la pregunta
Bryan
Esto es bueno, pero hubiera sido mejor si pudiera eliminar el único elemento de la matriz
Naguib Ihab
2
Solo un aviso, el intento de usar el primero de b (reemplazar con el último elemento) obviamente no funciona si está intentando eliminar el último elemento en la rebanada jajaja
Sirenas
13

En lugar de pensar en los índices en las anotaciones [a:]-, [:b]- y - [a:b]como índices de elementos, piense en ellos como los índices de los huecos alrededor y entre los elementos, comenzando con el espacio indexado 0antes del elemento indexado como 0.

ingrese la descripción de la imagen aquí

Mirando solo los números azules, es mucho más fácil ver lo que está sucediendo: [0:3]encierra todo, [3:3]está vacío y [1:2]cedería {"B"}. Entonces [a:]es solo la versión corta de [a:len(arrayOrSlice)], [:b]la versión corta de [0:b]y [:]la versión corta de [0:len(arrayOrSlice)]. Este último se usa comúnmente para convertir una matriz en un segmento cuando es necesario.

Zyl
fuente
1
Esto ayuda a explicar por qué la respuesta es "no" al comentario de themihai sobre la respuesta de Dave , refiriéndose a [i + 1:] incluso cuando se refiere al elemento i-ésimo: play.golang.org/p/E0lQ3jPcjX5
Nick P
5

... es sintaxis para argumentos variados.

Creo que es implementado por el cumplidor usando slice ( []Type), al igual que la función append:

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

cuando usas "elems" en "append", en realidad es un segmento (tipo []). Entonces " a = append(a[:0], a[1:]...)" significa " a = append(a[0:0], a[1:])"

a[0:0] es una rebanada que no tiene nada

a[1:] es "Hola2 Hola3"

Así es como funciona

frank.lin
fuente
2
a[0:0]no es nilsino una rebanada con 0 de longitud. a[0:0]será solamente ser nilsi aes nil.
icza
5

Estoy obteniendo un error de índice fuera de rango con la solución de respuesta aceptada. Motivo: cuando se inicia el rango, no es un valor iterativo uno por uno, es iterar por índice. Si modificó un segmento mientras está dentro del rango, provocará algún problema.

Vieja respuesta:

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)

Esperado :

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

Real:

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

Forma correcta (solución):

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)

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

timotew
fuente
3

En el wiki de golang, muestra algunos trucos para el corte, incluida la eliminación de un elemento del corte.

Enlace: ingrese la descripción del enlace aquí

Por ejemplo, a es el segmento en el que desea eliminar el elemento número i.

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

O

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