Hapus elemen dalam satu irisan

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

Bagaimana cara menghapus trik ini dengan fungsi append berfungsi?

Tampaknya itu meraih segalanya sebelum elemen pertama (array kosong)

Kemudian menambahkan semuanya setelah elemen pertama (posisi nol)

Apa yang ... (dot dot dot) lakukan?

Jorge Olivero
sumber
3
Mengapa tidak melihat spesifikasi bahasa? ...dijelaskan di sana secara detail?
Volker
9
Di satu sisi, benar-benar benar ( golang.org/ref/spec , semuanya); di sisi lain, ada cukup asing tentang idiom ini untuk bermigrasi Pythonistas, dll. Saya tidak keberatan memiliki penjelasan di luar sana untuk orang lain temukan.
twotwotwo
Lihat github.com/golang/go/wiki/SliceTricks
Evgeniy Tkachenko

Jawaban:

276

Di mana aslice, dan iindeks elemen yang ingin Anda hapus:

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

... adalah sintaks untuk argumen variadic di Go.

Pada dasarnya, ketika mendefinisikan suatu fungsi, ia menempatkan semua argumen yang Anda masukkan ke dalam satu irisan jenis itu. Dengan melakukan itu, Anda dapat memberikan argumen sebanyak yang Anda inginkan (misalnya, fmt.Printlndapat mengambil argumen sebanyak yang Anda inginkan).

Sekarang, ketika memanggil fungsi, ...lakukan yang sebaliknya: ia membongkar sebuah slice dan meneruskannya sebagai argumen terpisah ke fungsi variadic.

Jadi apa yang dilakukan garis ini:

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

pada dasarnya:

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

Sekarang, Anda mungkin bertanya-tanya, mengapa tidak lakukan saja

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

Nah, definisi fungsi appendadalah

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

Jadi argumen pertama harus berupa irisan dari tipe yang benar, argumen kedua adalah variadic, jadi kami meneruskan irisan kosong, dan kemudian membongkar sisa slice untuk mengisi argumen.

dave
sumber
35
Tidakkah Anda keluar dari rentang pengecualian jika saya adalah elemen terakhir dari slice? a = append(a[:i], a[i+1:]...)
themihai
5
@ DaveC Saya mendapatkan kesalahan itu ketika bekerja dengan irisan saya di proyek saya: /
Tyguy7
3
@ Tyguy7 dari spec: "Untuk array atau string, indeks berada dalam kisaran jika 0 <= rendah <= tinggi <= len (a), jika tidak maka di luar kisaran." Mungkin dalam kasus Anda tinggi <rendah; dalam hal ini Anda akan mendapatkan kesalahan. ( golang.org/ref/spec#Slice_expressions )
mlg
2
Bagaimana kinerjanya? Saya sungguh berharap itu tidak menciptakan irisan yang sama sekali baru di bawah tenda ..
joonas.fi
7
@ Tyguy7 Saya pikir Anda mencoba menghapus elemen slice dalam satu lingkaran. Jadi, Anda harus berhati-hati dengan indeks.
Nikolay Bystritskiy
42

Ada dua opsi:

A: Anda peduli tentang mempertahankan pesanan array:

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

B: Anda tidak peduli tentang mempertahankan pesanan (ini mungkin lebih cepat):

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.

Lihat tautan untuk melihat implikasi kebocoran memori jika array Anda adalah petunjuk.

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

Chris
sumber
Ini menarik, tetapi tidak benar-benar menjawab pertanyaan
Bryan
Ini bagus, tetapi akan lebih baik jika dapat menghapus satu-satunya elemen dalam array
Naguib Ihab
2
Hanya kepala, mencoba menggunakan yang pertama dari b (ganti dengan elemen terakhir) tidak jelas bekerja jika Anda mencoba untuk menghapus elemen terakhir di slice lol
Sirens
13

Daripada memikirkan indeks dalam [a:]-, [:b]- dan- [a:b]catatan sebagai indeks elemen, anggap mereka sebagai indeks kesenjangan di sekitar dan di antara elemen, dimulai dengan celah yang diindeks 0sebelum elemen diindeks sebagai 0.

masukkan deskripsi gambar di sini

Melihat angka biru saja, akan lebih mudah untuk melihat apa yang sedang terjadi: [0:3]menutup semuanya, [3:3]kosong dan [1:2]akan menghasilkan {"B"}. Kemudian [a:]hanya versi pendek [a:len(arrayOrSlice)], [:b]versi pendek [0:b]dan [:]versi pendek [0:len(arrayOrSlice)]. Yang terakhir ini biasanya digunakan untuk mengubah array menjadi slice saat dibutuhkan.

Zyl
sumber
1
Ini membantu menjelaskan mengapa jawabannya adalah "tidak" untuk komentar themihai pada jawaban dave , merujuk pada [i + 1:] bahkan ketika merujuk pada elemen ke-i: play.golang.org/p/E0lQ3jPcjX5
Nick P
5

... adalah sintaks untuk argumen variadic.

Saya pikir ini diimplementasikan oleh complier menggunakan slice ( []Type), sama seperti fungsi append:

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

ketika Anda menggunakan "elems" di "append", sebenarnya itu adalah slice ([] type). Jadi " a = append(a[:0], a[1:]...)" berarti " a = append(a[0:0], a[1:])"

a[0:0] adalah sepotong yang tidak memiliki apa-apa

a[1:] adalah "Hello2 Hello3"

Begini Cara kerjanya

frank.lin
sumber
2
a[0:0]tidak lain niladalah sepotong dengan panjang 0. a[0:0]akan hanya menjadi niljika aadalah nil.
icza
5

Saya mendapatkan indeks di luar rentang kesalahan dengan solusi jawaban yang diterima. Alasan: Ketika rentang mulai, itu bukan nilai iterate satu per satu, iterate by index. Jika Anda memodifikasi sepotong saat berada dalam jangkauan, itu akan menimbulkan beberapa masalah.

Jawaban lama:

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)

Diharapkan:

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

Sebenarnya:

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

Cara yang Benar (Solusi):

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)

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

timotew
sumber
3

Dalam wiki golang itu menunjukkan beberapa trik untuk slice, termasuk menghapus elemen dari slice.

Tautan: masukkan deskripsi tautan di sini

Misalnya a adalah irisan yang ingin Anda hapus elemen angka i.

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

ATAU

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