Golang'da bir haritadan bir parça değer almanın güzel bir yolu var mı?


90

Bir m haritam varsa, v değerlerinden bir dilim almanın daha iyi bir yolu var mı?

package main
import (
  "fmt"
)

func main() {
    m := make(map[int]string)

    m[1] = "a"
    m[2] = "b"
    m[3] = "c"
    m[4] = "d"

    // Can this be done better?
    v := make([]string, len(m), len(m))
    idx := 0
    for  _, value := range m {
       v[idx] = value
       idx++
    }

    fmt.Println(v)
 }

Bir haritanın yerleşik bir özelliği var mı? Go paketinde bir işlev var mı, yoksa yapmam gerekirse yapabileceğim en iyi kod bu mu?


1
for döngüsünüzde '_' yerine, buna idx deyin ve idx ++ işinden kurtulun
Peter Agnew

Hayır, yapamaz, bir harita üzerinde gezinirseniz anahtar, değer, indeks değil, değer döndürür. Örneğinde, ilk anahtar olarak 1'i kullanıyor ve bu, v dilimindeki dizinleri yanlış yapacak çünkü başlangıç ​​dizini sıfır değil 1 olacak ve 4'e ulaştığında aralık dışı olacaktır. play.golang.org/p/X8_SbgxK4VX
Popmedic

@Popmedic Aslında evet yapabilir. sadece dilim değerlerini atarken _ile değiştirin idxve kullanın idx-1.
hewiefreeman

3
@ newplayer66, bu çok tehlikeli bir model.
Popmedic

Yanıtlar:


60

Ne yazık ki hayır. Bunu yapmanın yerleşik bir yolu yok.

Yan not olarak, dilim oluştururken kapasite argümanını atlayabilirsiniz:

v := make([]string, len(m))

Kapasitenin buradaki uzunluk ile aynı olması ima edilmektedir.


1
Sanırım daha iyi bir yol var: stackoverflow.com/a/61953291/1162217
Lukas Lukac

58

Jimt'in gönderisine ek olarak:

appendDeğerleri açık bir şekilde indekslerine atamak yerine kullanabilirsiniz :

m := make(map[int]string)

m[1] = "a"
m[2] = "b"
m[3] = "c"
m[4] = "d"

v := make([]string, 0, len(m))

for  _, value := range m {
   v = append(v, value)
}

Uzunluğun sıfır olduğuna (henüz hiçbir öğe bulunmadığına), ancak kapasitenin (ayrılan alan) öğelerinin sayısıyla başlatıldığına dikkat edin m. Bu yapılır, böylelikle appenddilimin kapasitesi her bittiğinde bellek ayırmaya gerek yoktur v.

makeDilimi kapasite değeri olmadan da yapabilir appendve belleği kendisi için ayırabilirsiniz.


Bunun daha yavaş olup olmayacağını merak ediyordum (ön tahsisi varsayarsak)? Harita [int] int ile kaba bir kıyaslama yaptım ve yaklaşık% 1-2 daha yavaş görünüyordu. Bunun endişelenecek bir şey olup olmadığı veya onunla devam edeceğine dair bir fikrin var mı?
masebase

1
Eklemenin biraz daha yavaş olduğunu varsayabilirim, ancak bu fark çoğu durumda ihmal edilebilir. Doğrudan atama ve eklemeyi karşılaştıran kıyaslama .
nemo

1
Bunu yukarıdaki cevapla karıştırırken dikkatli olun - kullandıktan sonra diziye make([]appsv1.Deployment, len(d))eklemek, boş öğeler ayırdığınızda oluşturulan bir grup boş öğeye eklenecektir len(d).
Anirudh Ramanathan

2

Şart değil daha iyi, ancak bunu yapmak için temizleyici yolu hem tanımlayarak olduğunu Dilim UZUNLUĞU ve KAPASİTESİ gibitxs := make([]Tx, 0, len(txMap))

    // Defines the Slice capacity to match the Map elements count
    txs := make([]Tx, 0, len(txMap))

    for _, tx := range txMap {
        txs = append(txs, tx)
    }

Tam örnek:

package main

import (
    "github.com/davecgh/go-spew/spew"
)

type Tx struct {
    from  string
    to    string
    value uint64
}

func main() {
    // Extra touch pre-defining the Map length to avoid reallocation
    txMap := make(map[string]Tx, 3)
    txMap["tx1"] = Tx{"andrej", "babayaga", 10}
    txMap["tx2"] = Tx{"andrej", "babayaga", 20}
    txMap["tx3"] = Tx{"andrej", "babayaga", 30}

    txSlice := getTXsAsSlice(txMap)
    spew.Dump(txSlice)
}

func getTXsAsSlice(txMap map[string]Tx) []Tx {
    // Defines the Slice capacity to match the Map elements count
    txs := make([]Tx, 0, len(txMap))
    for _, tx := range txMap {
        txs = append(txs, tx)
    }

    return txs
}

Basit bir çözüm ama çok fazla sorun. Daha fazla ayrıntı için bu blog gönderisini okuyun: https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas


Cevap "yanlış" değil. Soru bir dizin kullanıyor ve dilim için ekleme yok. Dilim üzerinde append kullanırsanız, evet uzunluğu ön tarafa ayarlamak kötü olur. İşte soru şu olduğu gibi play.golang.org/p/nsIlIl24Irn Bu sorunun deyimsel go olmadığını kabul ettim ve hala öğreniyordum. Bahsettiğiniz şeye daha çok benzeyen biraz geliştirilmiş bir versiyon play.golang.org/p/4SKxC48wg2b
masebase

1
Merhaba @masebase, bunu "yanlış" olarak değerlendiriyorum çünkü "Maalesef hayır. Bunu yapmanın yerleşik bir yolu yok.", Ancak ikimiz de 2 yıl sonra işaret ettiğimiz "daha iyi" bir çözüm var - eki kullanarak () ve hem uzunluğu hem de kapasiteyi tanımlar. Ama ben bunu "yanlış" olarak adlandırmanın da doğru olmadığını anlıyorum. İlk cümlemi şu şekilde değiştireceğim: "İlle de daha iyi değil, ancak bunu yapmanın daha temiz yolu" ~ 10 geliştiriciyle konuştum ve herkes append () 'in yardımcı indeks kullanmadan haritayı dilime dönüştürmenin daha temiz bir yolu olduğunu kabul etti. Bunu gönderi sırasında da öğrendim
Lukas Lukac

1

Şu anda bildiğim kadarıyla, go'nun en az / iki / kopya yapmadan sonuç dizgesine dizeleri / baytları birleştirmek için bir yöntemi yok.

Şu anda bir [] bayt büyütmek zorundasınız çünkü tüm dizge değerleri sabittir, SONRA dilin bir 'kutsanmış' dizge nesnesi yaratması için dize yerleşikini kullanmanız gerekir, çünkü bir yerde bir şey için bir referans olabilir. [] baytı destekleyen adrese.

Bir [] bayt uygunsa, baytlar üzerinde çok küçük bir liderlik elde edebilirsiniz.

package main
import (
  "fmt"
)

func main() {
m := make(map[int]string)

m[1] = "a" ;    m[2] = "b" ;     m[3] = "c" ;    m[4] = "d"

ip := 0

/* If the elements of m are not all of fixed length you must use a method like this;
 * in that case also consider:
 * bytes.Join() and/or
 * strings.Join()
 * They are likely preferable for maintainability over small performance change.

for _, v := range m {
    ip += len(v)
}
*/

ip = len(m) * 1 // length of elements in m
r := make([]byte, ip, ip)
ip = 0
for  _, v := range m {
   ip += copy(r[ip:], v)
}

// r (return value) is currently a []byte, it mostly differs from 'string'
// in that it can be grown and has a different default fmt method.

fmt.Printf("%s\n", r)
}

1

Bu mapspaketi kullanabilirsiniz :

go get https://github.com/drgrib/maps

O zaman araman gereken tek şey

values := maps.GetValuesIntString(m)

Bu ortak mapkombinasyon için tip açısından güvenlidir . Şunları yapabilirsiniz generateBaşka herhangi bir tür için diğer tip-güvenli işlevleri mapkullanarak mapperaynı pakette aracı.

Tam açıklama: Bu paketin yaratıcısıyım. Bunu yarattım çünkü kendimi bu işlevleri mapdefalarca yeniden yazarken buldum .

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.