简体   繁体   English

在 Go 中如何从地图中获取一个值?

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

If I have a map m is there a better way of getting a slice of the values v than this?如果我有一张地图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)
 }

Is there a built-in feature of a map ? map有内置功能吗? Is there a function in a Go package, or is this the only way to do this? Go 包中是否有函数,或者这是唯一的方法?

As an addition to jimt's post:作为 jimt 帖子的补充:

You may also use append rather than explicitly assigning the values to their indices:您也可以使用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)
}

Note that the length is zero (no elements present yet) but the capacity (allocated space) is initialized with the number of elements of m .请注意,长度为零(尚无元素),但容量(分配的空间)使用m的元素数进行初始化。 This is done so append does not need to allocate memory each time the capacity of the slice v runs out.这样做是为了让append不需要在每次 slice v的容量用完时分配内存。

You could also make the slice without the capacity value and let append allocate the memory for itself.您还可以make没有容量值的切片,并让append为自己分配内存。

Unfortunately, no.抱歉不行。 There is no builtin way to do this.没有内置的方法可以做到这一点。

As a side note, you can omit the capacity argument in your slice creation:作为旁注,您可以在创建切片时省略容量参数:

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

The capacity is implied to be the same as the length here.这里暗示容量与长度相同。

Go 1.18去 1.18

You can use maps.Values from the golang.org/x/exp package.您可以使用maps.Values包中的golang.org/x/exp

Values returns the values of the map m. Values 返回映射 m 的值。 The values will be in an indeterminate order.这些值的顺序不确定。

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

The package exp includes experimental code.exp包括实验代码。 The signatures may or may not change in the future, and may or may not be promoted to the standard library.签名将来可能会或可能不会更改,并且可能会或可能不会被提升到标准库。

If you don't want to depend on an experimental package, you can easily implement it yourself.如果您不想依赖实验包,您可以轻松地自己实现它。 In fact, this code is a copy-paste from the exp package:事实上,这段代码是从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
}

Not necessarily better, but the cleaner way to do this is by defining both the Slice LENGTH and CAPACITY like txs := make([]Tx, 0, len(txMap))不一定更好,但更简洁的方法是定义切片长度和容量,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)
    }

Full example:完整示例:

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
}

Simple solution but a lot of gotchas.简单的解决方案,但有很多陷阱。 Read this blog post for more details: https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas阅读这篇博文了解更多详情: https ://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas

As far as I'm currently aware, go doesn't have a way method for concatenation of strings/bytes in to a resulting string without making at least /two/ copies.据我目前所知,go 没有一种方法可以将字符串/字节连接到生成的字符串中,而无需制作至少 /two/ 副本。

You currently have to grow a []byte since all string values are const, THEN you have to use the string builtin to have the language create a 'blessed' string object, which it will copy the buffer for since something somewhere could have a reference to the address backing the []byte.您目前必须增加一个 []byte,因为所有字符串值都是 const,然后您必须使用内置的字符串让语言创建一个“祝福”字符串对象,它将复制缓冲区,因为某处可能有引用到支持 [] 字节的地址。

If a []byte is suitable then you can gain a very slight lead over the bytes.Join function by making one allocation and doing the copy calls your self.如果 []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)
}

As of 1.18, this is the best way: https://stackoverflow.com/a/71635953/130427从 1.18 开始,这是最好的方法: https ://stackoverflow.com/a/71635953/130427

Pre 1.18 1.18 之前

You can use this maps package:您可以使用此maps包:

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

Then all you have to call is那么你只需要打电话

values := maps.GetValuesIntString(m)

It's type-safe for that common map combination.对于该常见的map组合,它是类型安全的。 You can generate other type-safe functions for any other type of map using the mapper tool in the same package.您可以使用同一包中的mapper工具为任何其他类型的map generate其他类型安全的函数。

Full disclosure: I am the creator of this package.完全披露:我是这个包的创建者。 I created it because I found myself rewriting these functions for map repeatedly.我创建它是因为我发现自己反复为map重写这些函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM