Comment supprimer un élément d'une tranche dans 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])
}

J'utilise cette commande pour supprimer un élément d'une tranche mais cela ne fonctionne pas, veuillez suggérer.



Réponses:


178

L'ordre compte

Si vous voulez garder votre tableau ordonné, vous devez décaler tous les éléments à droite de l'index de suppression de un vers la gauche. Espérons que cela peut être fait facilement à Golang:

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

Cependant, cela est inefficace car vous pouvez finir par déplacer tous les éléments, ce qui est coûteux.

L'ordre n'est pas important

Si vous ne vous souciez pas de la commande, vous avez la possibilité beaucoup plus rapide d'échanger l'élément à supprimer avec celui à la fin de la tranche, puis de renvoyer les n-1 premiers éléments:

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

Avec la méthode de reslicing, vider un tableau de 1 000 000 éléments prend 224s, avec celui-ci, cela ne prend que 0,06ns. Je soupçonne qu'en interne, aller ne change que la longueur de la tranche, sans la modifier.

Modifier 1

Notes rapides basées sur les commentaires ci-dessous (merci à eux!).

Comme le but est de supprimer un élément, lorsque l'ordre n'a pas d'importance, un seul swap est nécessaire, le second sera gaspillé:

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

De plus, cette réponse n'effectue pas de vérification des limites . Il attend un index valide en entrée. Cela signifie que des valeurs négatives ou des indices supérieurs ou égaux à len (s) provoqueront la panique de Go. Les tranches et les tableaux étant indexés à 0, la suppression du n-ième élément d'un tableau implique de fournir une entrée n-1 . Pour supprimer le premier élément, appelez remove (s, 0) , pour supprimer le second, appelez remove (s, 1) , et ainsi de suite.


20
On dirait que si vous ne vous souciez pas de conserver la tranche d'origine, vous n'auriez même pas besoin d'un échange et cela s[i] = s[len(s)-1]; return s[:len(s)-1]suffirait.
Brad Peabody

@bgp Il s'agit simplement de sauter (supprimer le dernier élément) de la tranche, et non de supprimer celle sous l'index fourni comme dans la question d'origine. De même, vous pouvez déplacer (supprimer le premier élément) avec return s[1:]lequel ne répond pas non plus à la question d'origine.
shadyyx

2
Hm, pas vraiment. Ceci: s[i] = s[len(s)-1]copie définitivement le dernier élément dans l'élément à l'index i. Ensuite, return s[:len(s)-1]renvoie la tranche sans le dernier élément. Deux déclarations là-bas.
Brad Peabody

1
Échoue avec len (arr) == 2 et l'élément à supprimer est le dernier: play.golang.org/p/WwD4PfUUjsM
zenocon

1
@zenocon Dans Golang, les tableaux sont à 0-index, ce qui signifie que les indices valides pour un tableau de longueur 2 sont 0 et 1. En effet, cette fonction ne vérifie pas les limites du tableau et s'attend à ce qu'un index valide soit fourni. Lorsque len (arr) == 2 , les arguments valides sont donc 0 ou 1 . Tout le reste déclencherait un accès hors limites et Go paniquera.
T.Claverie

41

Supprimez un élément de la tranche (c'est ce qu'on appelle '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
Il convient de souligner que cela s'appelle `` re-slicing '' et est relativement coûteux bien que ce soit la manière idiomatique de le faire en Go. Ne confondez pas cela avec une opération comme la suppression d'un nœud d'une liste chaînée car ce n'est pas cela et si vous comptez le faire beaucoup, en particulier avec de grandes collections, vous devriez envisager des conceptions alternatives qui l'évitent.
evanmcdonnal

Oui, c'est une manière idiomatique dans Golang, et même dans C / Assembly, supprimer un élément aléatoire d'un tableau trié coûte cher, vous devez déplacer (copier) tous les éléments de droite d'une position vers la gauche. Oui, dans certains cas d'utilisation, la liste liée est la meilleure solution pour supprimer un élément aléatoire de la liste.

1
Notez que cette méthode provoque toutes les modifications et maintenant n et tous pointent vers une partie du même tableau sous-jacent. Ceci est très susceptible d'entraîner des bogues dans votre code.
mschuett

1
Obtenir cette erreur2019/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
cela entraînerait panicsi l'index est le dernier élément de la tranche
STEEL

28

Point mineur (code golf), mais dans le cas où l'ordre n'a pas d'importance, vous n'avez pas besoin d'échanger les valeurs. Remplacez simplement la position du tableau supprimée par un double de la dernière position, puis renvoyez un tableau tronqué.

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

Même résultat.


13
L'implémentation la plus lisible serait de copier le premier élément dans l'index spécifié s[i] = s[0], puis de renvoyer un tableau avec uniquement les n-1 derniers éléments. return s[1:]
Kent

4
aire de jeux de la solution de
@Kent

2
@Kent, le problème avec le fait de faire s[1:]contre s[:len(s)-1]est que ce dernier fonctionnera beaucoup mieux si la tranche plus tard est appendmodifiée ou si les suppressions sont mélangées avec appends. Le dernier garde la capacité de tranche là où, contrairement au premier.
Dave C

1
Si vous supprimez le 0e élément avec cette fonction, le résultat est inversé.
ivarec

21

C'est un peu étrange à voir, mais la plupart des réponses ici sont dangereuses et ignorent ce qu'elles font réellement. En regardant la question d'origine qui a été posée sur la suppression d'un élément de la tranche, une copie de la tranche est en cours de création, puis remplie. Cela garantit que lorsque les tranches sont transmises à votre programme, vous n'introduisez pas de bogues subtils.

Voici un code comparant les réponses des utilisateurs dans ce fil de discussion et dans l'article original. Voici un terrain de jeu pour jouer avec ce code.

Ajouter la suppression basée

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

Dans l'exemple ci-dessus, vous pouvez me voir créer une tranche et la remplir manuellement avec les nombres 0 à 9. Nous supprimons ensuite l'index 5 de tous et l'assignons pour supprimer l'index. Cependant, lorsque nous allons tout imprimer maintenant, nous voyons qu'il a également été modifié. En effet, les tranches sont des pointeurs vers un tableau sous-jacent. L'écrire dans les removeIndexcauses allà modifier également avec la différence étant allest plus long par un élément qui n'est plus accessible à partir de removeIndex. Ensuite, nous changeons une valeur dans removeIndexet nous pouvons voir qu'elle allest également modifiée. Efficace va plus en détail à ce sujet.

Je n'entrerai pas dans l'exemple suivant, mais il fait la même chose pour nos besoins. Et illustre simplement que l'utilisation de la copie n'est pas différente.

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

Les questions réponse originale

En regardant la question d'origine, cela ne modifie pas la tranche dont il supprime un élément. Faire de la réponse originale dans ce fil la meilleure à ce jour pour la plupart des gens qui viennent sur cette page.

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

Comme vous pouvez le voir, cette sortie agit comme la plupart des gens s'y attendraient et probablement ce que la plupart des gens veulent. La modification de originalRemoven'entraîne pas de changements dans allet l'opération de suppression de l'index et de son attribution n'entraîne pas non plus de changements! Fantastique!

Ce code est un peu long, donc ce qui précède peut être changé en cela.

Une bonne réponse

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

Presque identique à la solution originale de suppression d'index, mais nous créons une nouvelle tranche à ajouter avant de revenir.


3
cela devrait être la réponse à la question car c'est la seule qui explique le risque de modifier le tableau de sauvegarde de la tranche
JessG

Dans la suppression basée sur l'ajout, la première tranche allest également modifiée après removeIndex := RemoveIndex(all, 5)car append réutilise le même tableau sous-jacent s'il a une capacité suffisante (la suppression d'un élément ne nécessite bien sûr pas plus de capacité). Inversement, si nous ajoutons des éléments et les attribuons removeIndex, append allouera un nouveau tableau et allne changera pas.
blackgreen le

12

C'est ainsi que vous supprimez d' une tranche à la manière du golang . Vous n'avez pas besoin de créer une fonction, elle est intégrée à l'append. Essayez-le ici 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

Extrait du livre The Go Programming Language

Pour supprimer un élément du milieu d'une tranche, en préservant l'ordre des éléments restants, utilisez la copie pour faire glisser les éléments de numéro supérieur vers le bas de un pour combler le vide:

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

3
Notez que cette méthode entraîne la modification de la tranche d'origine transmise.
mschuett

7

Je prends l'approche ci-dessous pour supprimer l'élément en tranche. Cela contribue à la lisibilité pour les autres. Et aussi immuable.

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

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

    return newitems
}

J'aime mieux cette approche que vous supprimez en fait toutes les occurrences de l'élément.
sortie le

Cela peut être facilement transformé en filtrant plusieurs éléments de la tranche, bien!
prélèvement eldad le

1

Peut-être que vous pouvez essayer cette méthode:

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

Usage:

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)

Résultat:

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

1
Probablement parce que ce n'est pas idiomatique et sur-conçu pour ce que la question pose. C'est une façon intéressante de le résoudre, mais personne ne devrait l'utiliser.
mschuett

1
Merci! En fait, cela a très bien fonctionné pour moi avec une interface, car cela semble être universel, peut
DADi590

1

Peut-être que ce code vous aidera.

Il supprime l'élément avec un index donné.

Prend le tableau et l'index à supprimer et renvoie un nouveau tableau à peu près comme la fonction d'ajout.

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

Ici, vous pouvez jouer avec le code: https://play.golang.org/p/aX1Qj40uTVs


1

la meilleure façon de le faire est d'utiliser la fonction 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

Trouvez un moyen ici sans déménager.

  • change l'ordre
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]
  • garder l'ordre
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

Pas besoin de vérifier chaque élément sauf si vous vous souciez du contenu et que vous pouvez utiliser slice append. Essaye le

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

voici l'exemple de terrain de jeu avec des pointeurs. 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)
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.