Golang'da bir Slice'dan bir eleman nasıl silinir


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

Bu komutu bir Slice'dan bir öğeyi silmek için kullanıyorum ama çalışmıyor, lütfen önerin.



Yanıtlar:


180

Sipariş önemlidir

Dizinizi sıralı tutmak istiyorsanız, silme indeksinin sağındaki tüm öğeleri birer birer sola kaydırmanız gerekir. Umarım bu Golang'da kolayca yapılabilir:

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

Bununla birlikte, bu verimsizdir çünkü tüm öğeleri hareket ettirmekle sonuçlanabilirsiniz, bu da maliyetli olabilir.

Düzen önemli değil

Sıralamayı önemsemiyorsanız, dilimin sonundaki ile silmek için öğeyi değiştirmek ve ardından n-1 ilk öğeleri döndürmek için çok daha hızlı olanağınız vardır:

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

Yeniden gönderme yöntemiyle, 1 000 000 öğeden oluşan bir diziyi boşaltmak 224 saniye alır, bununla yalnızca 0,06 ns alır. Git'in dahili olarak, dilimi değiştirmeden yalnızca uzunluğunu değiştirdiğinden şüpheleniyorum.

Düzenle 1

Aşağıdaki yorumlara dayalı kısa notlar (onlar sayesinde!).

Amaç bir öğeyi silmek olduğu için, sipariş önemli olmadığında tek bir takas gerektiğinde, ikincisi boşa harcanacaktır:

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

Ayrıca, bu cevap sınır kontrolü yapmaz . Giriş olarak geçerli bir indeks bekler. Bu, len (ler) e eşit veya daha büyük olan negatif değerlerin veya endekslerin Go paniğe neden olacağı anlamına gelir. Dilimlerin ve dizilerin 0 dizinli olması, bir dizinin n'inci elemanının kaldırılması, giriş n-1'in sağlanması anlamına gelir . İlk öğeyi kaldırmak için remove (s, 0) , ikinciyi kaldırmak için remove (s, 1) vb. Çağrıları yapın .


20
Görünüşe göre orijinal dilimi korumayı umursamıyorsanız, bir takasa bile ihtiyacınız s[i] = s[len(s)-1]; return s[:len(s)-1]olmayacak ve bu yeterli olacaktır.
Brad Peabody

@bgp Bu sadece bir haşhaş orijinal söz konusu olduğu gibi sağlanan endeks altında kaldırmadan, dilimin (son öğe kaldırma). Benzer şekilde , orijinal soruyu da yanıtlamayan kaydırabilirsiniz (ilk öğeyi kaldırabilirsiniz) return s[1:].
shadyyx

2
Hm, pek değil. Bu: s[i] = s[len(s)-1]kesinlikle son öğeyi dizindeki öğeye kopyalar i. Ardından, return s[:len(s)-1]son öğe olmadan dilimi döndürür. Orada iki ifade var.
Brad Peabody


1
@zenocon Golang'da, diziler 0-indeksidir, yani 2 uzunluklu bir dizi için geçerli indisler 0 ve 1'dir. Aslında, bu fonksiyon dizinin sınırlarını kontrol etmez ve geçerli bir indeksin sağlanmasını bekler. Tüm len (dizi) == 2 , geçerli argümanlar böylece olan 0 veya 1 . Başka herhangi bir şey sınır dışı erişimi tetikler ve Go paniğe kapılır.
T. Claverie

41

Dilimden bir öğeyi kaldırın (buna "yeniden dilimleme" denir):

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
Bunun 'yeniden dilimleme' olarak adlandırıldığına ve Go'da bunu yapmanın deyimsel yolu olsa da nispeten pahalı olduğuna dikkat çekmek gerekir. Bunu, bağlantılı bir listeden bir düğümü kaldırmak gibi bir işlemle karıştırmayın, çünkü bu değildir ve bunu çok yapacaksanız, özellikle büyük koleksiyonlarda, bundan kaçınan alternatif tasarımları düşünmelisiniz.
evanmcdonnal

Evet, Golang'da bu deyimsel bir yoldur ve C / Assembly'de bile sıralanmış diziden bir rastgele öğeyi çıkarmak pahalıdır, tüm sağ öğeleri bir konum sola kaydırmanız (kopyalamanız) gerekir. Evet, bazı kullanım durumlarında Bağlantılı Liste rastgele öğeleri Listeden çıkarmak için daha iyi bir çözümdür.

1
Bu yöntemin tümünün değiştirilmesine neden olduğunu ve şimdi n ve tümünün aynı temel dizinin bir parçasını işaret ettiğini unutmayın. Bu, kodunuzda hatalara yol açma olasılığı çok yüksektir.
mschuett

1
Bu hatayı alma2019/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
bu panic, dizinin Slice'daki son öğe olması durumunda ortaya çıkar
STEEL

28

Küçük nokta (golf kodu), ancak siparişin önemli olmadığı durumlarda değerleri değiştirmenize gerek yoktur. Sadece son konumun bir kopyası ile kaldırılmakta olan dizi konumunun üzerine yazın ve ardından kesilmiş bir dizi döndürün.

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

Aynı sonuç.


13
En okunaklı uygulama, ilk öğeyi belirtilen dizine kopyalamak ve s[i] = s[0]ardından yalnızca son n-1 öğelerini içeren bir dizi döndürmektir. return s[1:]
Kent

4
@Kent çözümünün oyun alanı
stevenspiel

2
@Kent'e s[1:]karşı yapmakla ilgili sorun s[:len(s)-1], dilim daha sonra appenddüzenlenirse veya silenler appends ile karıştırılırsa, daha sonra çok daha iyi performans gösterecektir . Daha sonra, dilim kapasitesini eskisinin olmadığı yerde tutar.
Dave C

1
Bu işlevle 0. elemanı kaldırırsanız, sonucu tersine çevirir.
ivarec

22

Bunu görmek biraz tuhaf ama buradaki cevapların çoğu tehlikelidir ve gerçekte ne yaptıklarıyla ilgilidir. Bir öğenin dilimden çıkarılmasıyla ilgili sorulan orijinal soruya bakıldığında, dilimin bir kopyası yapılıyor ve sonra dolduruluyor. Bu, dilimler programınızın etrafında dolaşırken ince hatalar oluşturmamanızı sağlar.

İşte bu konudaki ve orijinal gönderideki kullanıcıların cevaplarını karşılaştıran bazı kodlar. İşte bu kodla uğraşmak için bir oyun alanı .

Eklemeye dayalı kaldırma

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

Yukarıdaki örnekte benim bir dilim oluşturduğumu ve onu manuel olarak 0'dan 9'a kadar sayılarla doldurduğumu görebilirsiniz. Daha sonra indeks 5'i tümünden kaldırıp indeksi kaldırmak için atıyoruz. Ancak şimdi hepsini yazdırmaya gittiğimizde, onun da değiştirildiğini görüyoruz. Bunun nedeni, dilimlerin temeldeki bir diziye işaretçiler olmasıdır. Bunu, değiştirilecek removeIndexnedenlere yazmak all, fark varlığıyla birlikte, allartık erişilemeyen bir öğe tarafından artık removeIndex. Sonra içindeki bir değeri değiştiririz removeIndexve alldeğiştirildiğini de görebiliriz . Etkili geçiş bu konuda biraz daha ayrıntıya giriyor.

Aşağıdaki örneğe girmeyeceğim ama aynı şeyi bizim amaçlarımız için yapıyor. Ve sadece kopya kullanmanın farklı olmadığını gösteriyor.

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

Soruların orijinal cevabı

Orijinal soruya bakıldığında, bir öğeyi kaldırdığı dilimi değiştirmez. Bu başlıktaki orijinal yanıtı, bu sayfaya gelen çoğu insan için şimdiye kadarki en iyi yapmak.

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

Gördüğünüz gibi, bu çıktı çoğu insanın beklediği ve çoğu insanın istediği gibi davranıyor. 'Nin originalRemovedeğiştirilmesi, içinde değişikliklere neden olmaz allve dizinin kaldırılması ve atanması da değişikliklere neden olmaz! Fantastik!

Bu kod biraz uzun olsa da, yukarıdakiler buna değiştirilebilir.

Doğru cevap

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

Orijinal kaldırma indeksi çözümüyle neredeyse aynı, ancak geri dönmeden önce eklenecek yeni bir dilim oluşturuyoruz.


3

Ekleme tabanlı kaldırmada, ilk dilim allde değiştirilir, removeIndex := RemoveIndex(all, 5)çünkü append yeterli kapasiteye sahipse aynı temel diziyi yeniden kullanır (elbette bir öğeyi kaldırmak daha fazla kapasite gerektirmez). Tersine, eleman eklersek ve atarsak removeIndex, append yeni bir dizi tahsis eder ve alldeğişmez.
blackgreen

12

Bu nasıl Sil bir Gönderen dilim golang yolu . Ekte yerleşik olan bir işlev oluşturmanıza gerek yoktur. Burada deneyin 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

Go Programlama Dili kitabından

Bir dilimin ortasından bir öğeyi kaldırmak için, kalan öğelerin sırasını koruyarak, boşluğu doldurmak için daha yüksek numaralı öğeleri bir aşağı kaydırmak üzere kopyayı kullanın:

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

3
Bu yöntemin, geçirilen orijinal dilimin değiştirilmesine neden olduğunu unutmayın.
mschuett

7

Öğeyi dilimden çıkarmak için aşağıdaki yaklaşımı uyguluyorum. Bu, başkaları için okunabilirliğe yardımcı olur. Ve ayrıca değişmez.

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

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

    return newitems
}

Bu yaklaşımı daha çok beğendim, aslında öğenin tüm oluşumlarını kaldırıyorsunuz.
eexit

Bu, dilimdeki birkaç öğeyi filtrelemek için kolayca dönüştürülebilir, güzel!
eldad

1

Belki bu yöntemi deneyebilirsiniz:

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

Kullanım:

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)

Sonuç:

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

1
Muhtemelen deyimsel olmadığı ve sorunun sorduğu şey için fazla tasarlanmış olduğu için. Çözmenin ilginç bir yolu ama kimse bunu kullanmamalı.
mschuett

1
Teşekkürler! Aslında bir Arayüzle benim için gayet iyi çalıştı, çünkü bu evrensel gibi görünüyor, belki
DADi590

1

Belki bu kod yardımcı olur.

Belirli bir dizine sahip öğeyi siler.

Diziyi ve silinecek dizini alır ve hemen hemen append işlevine benzer yeni bir dizi döndürür.

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

Burada kodla oynayabilirsiniz: https://play.golang.org/p/aX1Qj40uTVs


1

bunu yapmanın en iyi yolu, append işlevini kullanmaktır:

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

Yer değiştirmeden burada bir yol bulun .

  • sırayı değiştirir
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]
  • düzeni sağlamak
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

İçeriği önemsemediğiniz ve dilim eklemeyi kullanamadığınız sürece her bir öğeyi kontrol etmeye gerek yok. denemek

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

İşte içinde işaretçiler bulunan oyun alanı örneği. 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)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.