在golang中,有一种从地图中获取值切片的好方法吗?


89

如果我有地图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)
 }

有地图的内置功能吗?Go软件包中是否有函数,或者如果需要的话,这是最好的代码吗?


1
而不是for循环中的“ _”,将其称为idx并放弃idx ++业务
Peter Agnew

不,他不能,当您在地图上移动时,它将返回键,值而不是索引,值。在他的示例中,他使用1作为第一个键,这将使片v中的索引不正确,因为起始索引将为1而不是零,并且当起始索引为4时将超出范围。play.golang.org/p/X8_SbgxK4VX
Popmedic

@Popmedic实际上,是的,他可以。分配切片值时只需替换_idx并使用idx-1
hewiefreeman

3
@ newplayer66,这是一个非常危险的模式。
Popmedic

Answers:



58

作为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为其分配内存。


我想知道这会不会更慢(假设预先分配)?我用map [int] int做了一个粗略的基准测试,它似乎慢了1-2%。有什么需要担心的想法吗?
masebase 2012年

1
我认为append会慢一点,但是在大多数情况下,该差异可以忽略不计。比较直接分配和追加的基准
nemo 2012年

1
请小心地将其与上述答案混合使用-使用后追加到数组make([]appsv1.Deployment, len(d))将追加到分配len(d)空项目时创建的一堆空元素。
拉马纳森

1

据我目前所知,go没有一种方法,可以将字符串/字节连接到结果字符串中,而无需至少复制/ two /个副本。

由于所有字符串值都是const,因此您当前必须增加[] byte,然后必须使用内置的字符串使该语言创建一个“ blessed”字符串对象,该对象将复制缓冲区,因为某些地方可以引用到支持[] 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)
}

1

您可以使用此maps软件包:

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

那你只需要打电话

values := maps.GetValuesIntString(m)

对于该常见map组合,它是类型安全的。您可以在同一软件包中generatemap使用该mapper工具的任何其他类型使用其他类型安全的功能。

全面披露:我是这个软件包的创建者。我创建它是因为我发现自己map反复重写了这些功能。


1

不一定好,但更清洁的方式做,这是通过定义两个片长度和容量喜欢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


答案不是“错误”。该问题使用索引,而切片未添加任何附件。如果在切片上使用append,则是,在前面设置长度会不好。这是一个问题,因为它是 play.golang.org/p/nsIlIl24Irn 授予该问题不是惯用的语言,我仍在学习。略有改进的版本更像您在说的是 play.golang.org/p/4SKxC48wg2b
masebase

1
@masebase,您好,我认为它是“错误的”,因为它指出:“不幸的是,没有。没有内置的方法可以执行此操作。”,但是两年后我们都指出了“更好的”解决方案-使用附录()并定义长度和容量。但我确实看到您的观点,称其“错误”也不正确。我将第一句话改为:“不一定更好,但是更干净的方法是”。我已经与约10个开发人员进行了交谈,每个人都同意append()是一种更干净的方法,可以在不使用帮助程序索引的情况下将地图转换为切片。我也学会了这个帖子上的方式
卢卡斯卢卡奇
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.