在Go中如何从map中获取值的切片?

huangapple go评论92阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2012年11月17日 02:47:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/13422578.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定