在Go语言中如何从Map中获取一个切片值?

ss2ws0br  于 2023-03-06  发布在  Go
关注(0)|答案(6)|浏览(184)

如果我有一个Mapm,有没有比这更好的方法来获取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语言包中是否有函数,或者这是唯一的方法?

lrpiutwd

lrpiutwd1#

作为对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不需要在每次片v的容量用完时分配存储器。
您还可以make不带容量值的片,并让append为自己分配内存。

zu0ti5jz

zu0ti5jz2#

不幸的是,没有。没有内置的方法可以做到这一点。
另外,您可以在创建切片时省略capacity参数:

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

这里暗示容量与长度相同。

jvidinwx

jvidinwx3#

开始1.18

您可以使用golang.org/x/exp包中的maps.Values
Values返回Mapm的值。这些值的顺序不确定。

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

exp包含实验代码。签名将来可能会也可能不会更改,并且可能会也可能不会升级到标准库。
如果你不想依赖于一个实验性的软件包,你可以很容易地自己实现它,事实上,下面的代码片段是一个复制粘贴的from the exp/maps package,最初由Ian Lance Taylor编写:

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

qvtsj1bj4#

不一定更好,但更简洁的方法是同时定义Slice LENGTH和CAPACITY,如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

mklgxw1f

mklgxw1f5#

就我目前所知,go没有一个way方法可以将字符串/字节连接成一个结果字符串,而不需要至少复制/two/。
你现在必须增长一个[]byte,因为所有的字符串值都是常量,然后你必须使用字符串内置函数让语言创建一个“受祝福的”字符串对象,它将复制缓冲区,因为某个地方可能有一个支持[]byte的地址的引用。
如果一个[]byte是合适的,那么你可以获得一个比byte稍微领先的位置。通过分配一个连接函数并进行复制调用你自己。

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

t98cgbkg6#

从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重写这些函数。

相关问题