簡體   English   中英

在 Go 中如何從地圖中獲取一個值?

[英]In Go how to get a slice of values from a map?

如果我有一張地圖m有沒有比這更好的方法來獲取值v的一部分?

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

map有內置功能嗎? Go 包中是否有函數,或者這是唯一的方法?

作為 jimt 帖子的補充:

您也可以使用append而不是顯式地將值分配給它們的索引:

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

請注意,長度為零(尚無元素),但容量(分配的空間)使用m的元素數進行初始化。 這樣做是為了讓append不需要在每次 slice v的容量用完時分配內存。

您還可以make沒有容量值的切片,並讓append為自己分配內存。

抱歉不行。 沒有內置的方法可以做到這一點。

作為旁注,您可以在創建切片時省略容量參數:

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

這里暗示容量與長度相同。

去 1.18

您可以使用maps.Values包中的golang.org/x/exp

Values 返回映射 m 的值。 這些值的順序不確定。

func main() {
    m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d"}
    v := maps.Values(m)
    fmt.Println(v) 
}

exp包括實驗代碼。 簽名將來可能會或可能不會更改,並且可能會或可能不會被提升到標准庫。

如果您不想依賴實驗包,您可以輕松地自己實現它。 事實上,這段代碼是從exp包中復制粘貼的:

func Values[M ~map[K]V, K comparable, V any](m M) []V {
    r := make([]V, 0, len(m))
    for _, v := range m {
        r = append(r, v)
    }
    return r
}

不一定更好,但更簡潔的方法是定義切片長度和容量,txs := 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)
    }

完整示例:

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
}

簡單的解決方案,但有很多陷阱。 閱讀這篇博文了解更多詳情: https ://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas

據我目前所知,go 沒有一種方法可以將字符串/字節連接到生成的字符串中,而無需制作至少 /two/ 副本。

您目前必須增加一個 []byte,因為所有字符串值都是 const,然后您必須使用內置的字符串讓語言創建一個“祝福”字符串對象,它將復制緩沖區,因為某處可能有引用到支持 [] 字節的地址。

如果 []byte 是合適的,那么您可以在 bytes.Join 函數中獲得非常小的領先優勢,方法是進行一次分配並進行復制調用您自己。

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.18 開始,這是最好的方法: https ://stackoverflow.com/a/71635953/130427

1.18 之前

您可以使用此maps包:

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

那么你只需要打電話

values := maps.GetValuesIntString(m)

對於該常見的map組合,它是類型安全的。 您可以使用同一包中的mapper工具為任何其他類型的map generate其他類型安全的函數。

完全披露:我是這個包的創建者。 我創建它是因為我發現自己反復為map重寫這些函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM