スライスの要素を削除する


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

追加機能を使用したこの削除トリックはどのように機能しますか?

それは最初の要素(空の配列)の前にすべてをつかんでいるようです

次に、最初の要素(位置0)の後にすべてを追加します。

...(ドットドットドット)は何をしますか?



3
言語仕様を見てみませんか? ...そこに詳しく説明されていますか?
Volker 14

9
一方では、完全に真実です(golang.org/ref/spec、みんな); もう1つは、Pythonistaなどを移行するためのこれらのイディオムについて十分に不慣れであり、他の人が見つけるための説明があっても構わないと思います。
twotwotwo 14

回答:


276

どこaスライスであり、i削除したい要素のインデックスです。

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

... Goの可変引数の構文です。

基本的に、関数を定義すると、渡したすべての引数がその型の1つのスライスに入れられます。そうすることで、必要な数の引数を渡すことができます(たとえば、fmt.Println必要な数の引数を取ることができます)。

これで、関数を呼び出すときに...、反対のことが行われます。スライスがアンパックされ、可変個の関数に個別の引数として渡されます。

したがって、この行は何をしますか:

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

本質的に:

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

さて、あなたは疑問に思うかもしれません、なぜただしないのですか?

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

さて、の関数定義append

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

したがって、最初の引数は正しいタイプのスライスでなければならず、2番目の引数は可変長であるため、空のスライスを渡してから、残りのスライスをアンパックして引数を入力します。


35
私がスライスの最後の要素である場合、範囲外の例外が発生しませんか?a = append(a[:i], a[i+1:]...)
themihai

5
@DaveCプロジェクトでスライスを操作するときにエラーが発生します:/
Tyguy7

3
仕様の@ Tyguy7:「配列または文字列の場合、インデックスは0 <=低<=高<= len(a)の場合は範囲​​内にあり、それ以外の場合は範囲​​外です。」多分あなたの場合高<低; その場合、エラーが発生します。(golang.org/ref/spec#Slice_expressions
MLG

2
これのパフォーマンスはいかがですか?私は真剣に...それはボンネットの下に完全に新しいスライスを作成していない願っています
joonas.fi

7
@ Tyguy7ループ内でスライスの要素を削除しようとしたと思います。したがって、インデックスには注意する必要があります。
Nikolay Bystritskiy 2017年

42

2つのオプションがあります。

A:配列の順序を保持する必要があります。

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

B:順序を保持する必要はありません(これはおそらくより高速です)。

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.

リンクがポインタの配列である場合のメモリリークの影響を確認するには、リンクを参照してください。

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


これは興味深いですが、実際には質問に答えていません
ブライアン

これはいいのですが、配列内の唯一の要素を削除できればより良いでしょう
Naguib Ihab

2
ヘッズアップ、bの最初の要素(最後の要素で置換)を使用しようとしても、スライスの最後の要素を削除しようとしても明らかに機能しませんlol
Sirens

13

むしろにおける指標の考え方よりも[a:]- 、[:b]-および[a:b]要素インデックスとして-notations、索引付けギャップで始まる、周囲および要素間のギャップの指標と考える0ように索引付け要素の前に0

ここに画像の説明を入力してください

青い数字だけを見ると、何が起こっているのかをはるかに簡単に確認できます。[0:3]すべてを囲み、[3:3]空であり、[1:2]が生成され{"B"}ます。次に[a:]、のショートバージョン[a:len(arrayOrSlice)][:b]のショートバージョン、[0:b]および[:]のショートバージョンです[0:len(arrayOrSlice)]。後者は一般に、必要に応じて配列をスライスに変換するために使用されます。


1
これは、daveの回答に関するthemihaiのコメントに対する回答が「いいえ」である理由を説明するのに役立ちます。i 番目の要素を参照する場合でも、[i + 1:]を参照してください。play.golang.org
p

5

...は可変引数の構文です。

私はそれがスライスを使用するコンパイラによって実装されていると思い[]Type)ます

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

「append」で「elems」を使用すると、実際にはスライス([]タイプ)になります。「だから、a = append(a[:0], a[1:]...)」手段「a = append(a[0:0], a[1:])

a[0:0] 何もないスライスです

a[1:] 「Hello2 Hello3」です

これがどのように機能するか


2
a[0:0]nil長さ0のスライスではありません。a[0:0]唯一のことnil場合aですnil
icza

5

承認された回答ソリューションで範囲外エラーのインデックスを取得しています。理由:範囲の開始時に、値を1つずつ反復するのではなく、インデックスによって反復します。範囲内にあるスライスを変更すると、問題が発生します。

古い答え:

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)

期待される:

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

実際:

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

正しい方法(ソリューション):

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)

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


3

golangのwikiでは、スライスから要素を削除するなど、スライスに関するいくつかのトリックを示しています。

リンク:リンクの説明をここに入力してください

たとえば、aは番号i要素を削除するスライスです。

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

または

a = a[:i+copy(a[i:], a[i+1:])]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.