英文:
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"
// 是否有更好的方法?
v := make([]string, len(m), len(m))
idx := 0
for _, value := range m {
v[idx] = value
idx++
}
fmt.Println(v)
}
有没有map
的内置功能? Go包中是否有一个函数,还是这是唯一的方法?
英文:
If I have a map m
is there a better way of getting a slice of the values v
than this?
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
? Is there a function in a Go package, or is this the only way to do this?
答案1
得分: 91
作为对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
为其自己分配内存。
英文:
As an addition to jimt's post:
You may also use append
rather than explicitly assigning the values to their indices:
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
. This is done so append
does not need to allocate memory each time the capacity of the slice v
runs out.
You could also make
the slice without the capacity value and let append
allocate the memory for itself.
答案2
得分: 77
很遗憾,没有。没有内置的方法来做到这一点。
另外,你可以在创建切片时省略容量参数:
v := make([]string, len(m))
这里的容量被隐含为与长度相同。
英文:
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.
答案3
得分: 47
Go 1.18
您可以使用golang.org/x/exp
包中的maps.Values
。
> 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/maps
包中复制粘贴的,最初由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
}
英文:
Go 1.18
You can use maps.Values
from the golang.org/x/exp
package.
> Values returns the values of the map 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. 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, the following code snippet is a copy-paste from the exp/maps
package, originally authored by 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
}
答案4
得分: 10
不一定更好,但更干净的方法是通过定义切片的长度和容量,例如 txs := make([]Tx, 0, len(txMap))
// 将切片的容量定义为与映射元素数量相匹配
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() {
// 预先定义映射长度以避免重新分配内存
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 {
// 将切片的容量定义为与映射元素数量相匹配
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
英文:
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))
// 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
答案5
得分: 1
根据我目前的了解,Go语言没有一种方法可以将字符串/字节连接成一个结果字符串而不至少进行两次复制。
你目前必须扩展一个[]byte,因为所有的字符串值都是常量,然后你必须使用string内置函数让语言创建一个“blessed”字符串对象,它将复制缓冲区,因为某个地方可能有一个引用指向[]byte的地址。
如果[]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
/* 如果m的元素不都是固定长度的,你必须使用这样的方法;
* 在这种情况下,还要考虑:
* bytes.Join() 和/或
* strings.Join()
* 它们可能更适合于可维护性,而不是小的性能改变。
for _, v := range m {
ip += len(v)
}
*/
ip = len(m) * 1 // m中元素的长度
r := make([]byte, ip, ip)
ip = 0
for _, v := range m {
ip += copy(r[ip:], v)
}
// r(返回值)目前是一个[]byte,它与'string'主要不同之处在于它可以增长,并且有一个不同的默认fmt方法。
fmt.Printf("%s\n", r)
}
英文:
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.
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.
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.
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)
}
答案6
得分: 1
从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
生成其他类型安全的函数。
完全透明:我是这个包的创建者。我创建它是因为我发现自己反复为map
重写这些函数。
英文:
As of 1.18, this is the best way:
https://stackoverflow.com/a/71635953/130427
Pre 1.18
You can use this maps
package:
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. You can generate
other type-safe functions for any other type of map
using the mapper
tool in the same package.
Full disclosure: I am the creator of this package. I created it because I found myself rewriting these functions for map
repeatedly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论