web-dev-qa-db-fra.com

Supprimer un élément dans une tranche

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

Comment cette astuce de suppression avec la fonction append fonctionne-t-elle?

Il semblerait que tout soit avant le premier élément (tableau vide)

Puis tout ajouter après le premier élément (position zéro)

Que fait le ... (point, point, point)?

132
Jorge Olivero

a est la tranche et i est l'index de l'élément que vous voulez supprimer:

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

... est la syntaxe des arguments variadiques dans Go.

Fondamentalement, quand définissant une fonction, tous les arguments que vous transmettez sont placés dans une tranche de ce type. En faisant cela, vous pouvez passer autant d'arguments que vous le souhaitez (par exemple, fmt.Println peut prendre autant d'arguments que vous le souhaitez).

Maintenant, quand appelant une fonction, ... fait l'inverse: il décompresse une tranche et les transmet sous forme d'arguments séparés à une fonction variadique.

Alors qu'est-ce que cette ligne fait:

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

est essentiellement:

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

Maintenant, vous vous demandez peut-être, pourquoi ne pas simplement faire

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

Eh bien, la définition de la fonction de append est

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

Ainsi, le premier argument doit être une tranche du type correct, le deuxième argument est la variable, de sorte que nous passons dans une tranche vide, puis décompressons le reste de la tranche pour compléter les arguments.

262
dave

Il y a deux options:

R: Vous tenez à conserver l'ordre du tableau:

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

B: Vous ne vous souciez pas de maintenir l'ordre (c'est probablement plus rapide):

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.

Voir le lien pour voir les implications sur les fuites de mémoire si votre tableau est constitué de pointeurs.

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

42
Chris

Plutôt que de considérer les indices dans les notations [a:]-, [:b]- et [a:b]- en tant qu'indices d'éléments, considérez-les comme des indices des écarts autour et entre les éléments, en commençant par écart indexé 0 avant l'élément indexé sous la forme 0.

enter image description here

En regardant seulement les chiffres bleus, il est beaucoup plus facile de voir ce qui se passe: [0:3] contient tout, [3:3] est vide et [1:2] donnerait {"B"}. Alors [a:] n'est que la version abrégée de [a:len(arrayOrSlice)], [:b] la version abrégée de [0:b] et [:] la version abrégée de [0:len(arrayOrSlice)]. Ce dernier est couramment utilisé pour transformer un tableau en une tranche en cas de besoin.

9
Zyl

... est la syntaxe des arguments variadiques.

Je pense qu'il est implémenté par le compliant en utilisant slice ([]Type), comme la fonction append:

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

quand vous utilisez "elems" dans "append", c'est en fait une tranche (type []). Donc "a = append(a[:0], a[1:]...)" signifie "a = append(a[0:0], a[1:])"

a[0:0] est une tranche qui n'a rien

a[1:] est "Hello2 Hello3"

Voilà comment cela fonctionne

5
frank.lin

Je reçois une erreur d'index hors de portée avec la solution de réponse acceptée. Raison: lorsque la plage commence, il ne s'agit pas d'une itération, mais d'une itération par index. Si vous modifiez une tranche alors qu'elle se trouve dans la plage, cela posera un problème.

Ancienne réponse:

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)

Attendu:

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

Réel:

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

Manière correcte (solution):

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)

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

3
timotew

Dans le wiki de golang, il présente quelques astuces pour la découpe, notamment la suppression d'un élément de la découpe.

Lien: entrez la description du lien ici

Par exemple, a est la tranche pour laquelle vous voulez supprimer l’élément numéro i.

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

OR

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

Ou puisque vous essayez de trouver l'index de l'élément à supprimer de toute façon,

// na = new a, da = a that's to be deleted
var na []string
for _, v := range a {
    if v == da {
        continue
    } else {
        na = append(na, v)
    }
}
a = na

OK, c'est pas grave. Bonne réponse pour le sujet, mais mauvaise réponse pour le corps de la question.

2
user2312578