Excluir elemento em uma fatia

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

Como esse truque de exclusão com a função de acréscimo funciona?

Parece que está pegando tudo antes do primeiro elemento (matriz vazia)

Em seguida, anexando tudo após o primeiro elemento (posição zero)

O que o ... (ponto ponto ponto) faz?

Jorge Olivero
fonte
3
Por que não dar uma olhada nas especificações de idioma? ...é explicado lá em detalhes?
Volker
9
Por um lado, absolutamente verdadeiro ( golang.org/ref/spec , pessoal); por outro, não há familiaridade suficiente com esses idiomas para migrar Pythonistas, etc., que não me importo de ter uma explicação para outros encontrarem.
twotwotwo
Veja github.com/golang/go/wiki/SliceTricks
Evgeniy Tkachenko

Respostas:

276

Onde aestá a fatia e io índice do elemento que você deseja excluir:

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

... é uma sintaxe para argumentos variados no Go.

Basicamente, ao definir uma função, coloca todos os argumentos que você passa em uma fatia desse tipo. Ao fazer isso, você pode passar quantos argumentos quiser (por exemplo, fmt.Printlnpode usar quantos argumentos quiser).

Agora, ao chamar uma função, ...faz o oposto: descompacta uma fatia e as passa como argumentos separados para uma função variável.

Então, o que essa linha faz:

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

é essencialmente:

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

Agora, você pode estar se perguntando, por que não fazer

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

Bem, a definição de função de appendé

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

Portanto, o primeiro argumento deve ser uma fatia do tipo correto, o segundo argumento é o variável, então passamos uma fatia vazia e descompactamos o restante da fatia para preencher os argumentos.

Dave
fonte
35
Você não sai da exceção de faixa se eu for o último elemento da fatia? a = append(a[:i], a[i+1:]...)
themihai 21/05
5
@DaveC Eu recebo esse erro ao trabalhar com minhas fatias no meu projeto: /
Tyguy7
3
@ Tyguy7 da especificação: "Para matrizes ou seqüências de caracteres, os índices estão no intervalo se 0 <= baixo <= alto <= len (a), caso contrário, eles estão fora do intervalo." Talvez no seu caso seja alto <baixo; nesse caso, você receberá o erro. ( golang.org/ref/spec#Slice_expressions )
mlg 08/01
2
Como está o desempenho disso? Eu espero seriamente que não esteja criando uma fatia inteiramente nova sob o capô .. #
1128 joonas.fi
7
@ Tyguy7 Acho que você tentou excluir elementos de fatia dentro de um loop. Então você tem que ter cuidado com os índices.
Nikolay Bystritskiy
42

Existem duas opções:

R: Você se preocupa em manter a ordem da matriz:

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

B: Você não se preocupa em manter a ordem (isso provavelmente é mais 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.

Veja o link para ver implicações sobre vazamentos de memória se sua matriz for de ponteiros.

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

Chris
fonte
Isso é interessante, mas não realmente responder a pergunta
Bryan
Isso é bom, mas teria sido melhor se ele pode excluir o único elemento na matriz
Naguib Ihab
2
Apenas um heads-up, tentou usar a primeira de b (substitua com o último elemento) não obviamente trabalho, se você está tentando remover o último elemento da fatia lol
Sirens
13

Em vez de pensar nos índices nas anotações [a:]-, [:b]- e - [a:b]como índices de elementos, pense neles como os índices das lacunas ao redor e entre os elementos, começando com a lacuna indexada 0antes do elemento indexado como 0.

insira a descrição da imagem aqui

Olhando apenas os números azuis, é muito mais fácil ver o que está acontecendo: [0:3]encerra tudo, [3:3]está vazio e [1:2]renderia {"B"}. Então [a:]é apenas a versão curta [a:len(arrayOrSlice)], [:b]a versão curta [0:b]e [:]a versão curta de [0:len(arrayOrSlice)]. O último é comumente usado para transformar uma matriz em uma fatia quando necessário.

Zyl
fonte
1
Isso ajuda a explicar por que a resposta é "não" ao comentário de themihai na resposta de dave , referindo-se a [i + 1:] mesmo quando se refere ao i-ésimo elemento: play.golang.org/p/E0lQ3jPcjX5
Nick P
5

... é uma sintaxe para argumentos variados.

Eu acho que é implementado pelo complier usando slice ( []Type), assim como a função anexa:

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

quando você usa "elems" em "anexar", na verdade é uma fatia (do tipo []). Então " a = append(a[:0], a[1:]...)" significa " a = append(a[0:0], a[1:])"

a[0:0] é uma fatia que não tem nada

a[1:] é "Hello2 Hello3"

É assim que funciona

frank.lin
fonte
2
a[0:0]não é nilsenão uma fatia com 0 comprimento. a[0:0]será unicamente ser nilse aé nil.
icza
5

Estou recebendo um erro de índice fora do intervalo com a solução de resposta aceita. Motivo: quando o intervalo inicia, não é o valor da iteração um por um, é a iteração pelo índice. Se você modificou uma fatia enquanto estiver dentro do alcance, isso causará algum problema.

Resposta antiga:

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]

Maneira correta (solução):

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)

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

timotew
fonte
3

No wiki do golang, ele mostra alguns truques para a fatia, incluindo a exclusão de um elemento da fatia.

Link: insira a descrição do link aqui

Por exemplo, a é a fatia que você deseja excluir o elemento número i.

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

OU

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