从映射中获取键的片段

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

Getting a slice of keys from a map

问题

在Go语言中,从map中获取键的切片的更简单/更好的方法吗?

目前,我正在遍历map并将键复制到一个切片中:

i := 0
keys := make([]int, len(mymap))
for k := range mymap {
    keys[i] = k
    i++
}
英文:

<!-- language-all: lang-golang -->

Is there any simpler/nicer way of getting a slice of keys from a map in Go?

Currently I am iterating over the map and copying the keys to a slice:

i := 0
keys := make([]int, len(mymap))
for k := range mymap {
    keys[i] = k
    i++
}

答案1

得分: 546

这是一个老问题,但是这是我的个人意见。PeterSO的答案更简洁一些,但效率稍低。由于你已经知道它的大小,所以甚至不需要使用append:

keys := make([]int, len(mymap))

i := 0
for k := range mymap {
    keys[i] = k
    i++
}

在大多数情况下,这可能不会有太大的区别,但这并不需要太多的工作量。在我的测试中(使用一个包含1,000,000个随机int64键的映射,并使用每种方法生成键数组十次),直接分配数组成员比使用append快约20%。

尽管设置容量可以消除重新分配的操作,但每次追加时,append仍然需要额外的工作来检查是否已达到容量。

英文:

This is an old question, but here's my two cents. PeterSO's answer is slightly more concise, but slightly less efficient. You already know how big it's going to be so you don't even need to use append:

keys := make([]int, len(mymap))

i := 0
for k := range mymap {
    keys[i] = k
    i++
}

In most situations it probably won't make much of a difference, but it's not much more work, and in my tests (using a map with 1,000,000 random int64 keys and then generating the array of keys ten times with each method), it was about 20% faster to assign members of the array directly than to use append.

Although setting the capacity eliminates reallocations, append still has to do extra work to check if you've reached capacity on each append.

答案2

得分: 303

例如,

package main

func main() {
    mymap := make(map[int]string)
    keys := make([]int, 0, len(mymap))
    for k := range mymap {
        keys = append(keys, k)
    }
}

在Go语言中,为了提高效率,重要的是要尽量减少内存分配。

英文:

For example,

package main

func main() {
    mymap := make(map[int]string)
    keys := make([]int, 0, len(mymap))
    for k := range mymap {
        keys = append(keys, k)
    }
}

To be efficient in Go, it's important to minimize memory allocations.

答案3

得分: 124

你也可以通过reflect包中Value结构体的MapKeys方法,使用类型为[]Value的键数组:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    abc := map[string]int{
        "a": 1,
        "b": 2,
        "c": 3,
    }
    
    keys := reflect.ValueOf(abc).MapKeys()
    
    fmt.Println(keys) // [a b c]
}

这段代码可以获取map的所有键,并将其存储在keys数组中。最后,通过fmt.Println打印出keys数组的内容。

英文:

You also can take an array of keys with type []Value by method MapKeys of struct Value from package "reflect":

package main

import (
    &quot;fmt&quot;
    &quot;reflect&quot;
)

func main() {
	abc := map[string]int{
		&quot;a&quot;: 1,
		&quot;b&quot;: 2,
		&quot;c&quot;: 3,
	}
	
	keys := reflect.ValueOf(abc).MapKeys()
	
	fmt.Println(keys) // [a b c]
}

答案4

得分: 74

现在Go语言已经支持泛型了。你可以使用maps.Keys来获取任何映射的键。

以下是示例用法:

intMap := map[int]int{1: 1, 2: 2}
intKeys := maps.Keys(intMap)
// intKeys 是 []int 类型
fmt.Println(intKeys)

strMap := map[string]int{"alpha": 1, "bravo": 2}
strKeys := maps.Keys(strMap)
// strKeys 是 []string 类型
fmt.Println(strKeys)

maps包可以在golang.org/x/exp/maps中找到。这是一个实验性的包,不在Go的兼容性保证范围内。他们计划在将来的某个时候将其移入标准库中。

Playground: https://go.dev/play/p/fkm9PrJYTly

对于那些不喜欢导入实验性包的人,这里有源代码(最初由Ian Lance Taylor编写),你可以看到它非常简单:

// Keys 返回映射 m 的键。
// 键的顺序是不确定的。
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
	r := make([]K, 0, len(m))
	for k := range m {
		r = append(r, k)
	}
	return r
}
英文:

Go now has generics. You can get the keys of any map with maps.Keys.

Example usage:

	intMap := map[int]int{1: 1, 2: 2}
	intKeys := maps.Keys(intMap)
	// intKeys is []int
	fmt.Println(intKeys)

	strMap := map[string]int{&quot;alpha&quot;: 1, &quot;bravo&quot;: 2}
	strKeys := maps.Keys(strMap)
	// strKeys is []string
	fmt.Println(strKeys)

maps package is found in golang.org/x/exp/maps. This is experimental and outside of Go compatibility guarantee. They aim to move it into the std lib in <strike>Go 1.19</strike> the future.

Playground: https://go.dev/play/p/fkm9PrJYTly

For those who don't like to import exp packages, here's the source code (originally authored by Ian Lance Taylor), which as you can see is very simple:

// Keys returns the keys of the map m.
// The keys will be an indeterminate order.
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
	r := make([]K, 0, len(m))
	for k := range m {
		r = append(r, k)
	}
	return r
}

答案5

得分: 24

我对其他回答中描述的三种方法进行了一个简单的基准测试。

显然,在提取键之前预先分配切片要比使用append函数更快,但令人惊讶的是,reflect.ValueOf(m).MapKeys()方法比后者慢得多:

❯ go run scratch.go
正在填充
填充 100000000 个槽位
完成,用时 56.630774791 秒
运行预分配
用时:9.989049786 秒
运行追加
用时:18.948676741 秒
运行反射
用时:25.50070649 秒

这是代码:https://play.golang.org/p/Z8O6a2jyfTH
(在 playground 中运行会因为时间过长而中止,所以最好在本地运行)

英文:

I made a sketchy benchmark on the three methods described in other responses.

Obviously pre-allocating the slice before pulling the keys is faster than appending, but surprisingly, the reflect.ValueOf(m).MapKeys() method is significantly slower than the latter:

❯ go run scratch.go
populating
filling 100000000 slots
done in 56.630774791s
running prealloc
took: 9.989049786s
running append
took: 18.948676741s
running reflect
took: 25.50070649s

Here's the code: https://play.golang.org/p/Z8O6a2jyfTH
(running it in the playground aborts claiming that it takes too long, so, well, run it locally.)

答案6

得分: 18

一个更好的方法是使用append

keys := []int{}
for k := range mymap {
    keys = append(keys, k)
}

除此之外,你就没什么办法了——Go语言并不是一种非常表达性的语言。

英文:

A nicer way to do this would be to use append:

keys = []int{}
for k := range mymap {
    keys = append(keys, k)
}

Other than that, you’re out of luck—Go isn’t a very expressive language.

答案7

得分: 3

一个通用版本(go 1.18+)的Vinay Pai答案

// MapKeysToSlice将map的键提取为切片,
func MapKeysToSlice[K comparable, V any](m map[K]V) []K {
    keys := make([]K, len(m))

    i := 0
    for k := range m {
        keys[i] = k
        i++
    }
    return keys
}
英文:

A generic version (go 1.18+) of Vinay Pai's answer.

// MapKeysToSlice extract keys of map as slice,
func MapKeysToSlice[K comparable, V any](m map[K]V) []K {
    keys := make([]K, len(m))

    i := 0
    for k := range m {
        keys[i] = k
        i++
    }
    return keys
}

答案8

得分: 2

请访问 https://play.golang.org/p/dx6PTtuBXQW

package main

import (
	"fmt"
	"sort"
)

func main() {
	mapEg := map[string]string{"c":"a","a":"c","b":"b"}
	keys := make([]string, 0, len(mapEg))
	for k := range mapEg {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	fmt.Println(keys)
}
英文:

Visit https://play.golang.org/p/dx6PTtuBXQW

package main

import (
	&quot;fmt&quot;
	&quot;sort&quot;
)

func main() {
	mapEg := map[string]string{&quot;c&quot;:&quot;a&quot;,&quot;a&quot;:&quot;c&quot;,&quot;b&quot;:&quot;b&quot;}
	keys := make([]string, 0, len(mapEg))
	for k := range mapEg {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	fmt.Println(keys)
}

答案9

得分: 1

有一个很酷的库叫做lo

> 一个基于Go 1.18+泛型(map、filter、contains、find等)的Lodash风格的Go库

使用这个库,你可以进行许多方便的操作,比如map、filter、reduce等。还有一些用于map类型的辅助函数。

Keys

创建一个包含map键的数组。

keys := lo.Keys[string, int](map[string]int{"foo": 1, "bar": 2})
// []string{"bar", "foo"}

Values

创建一个包含map值的数组。

values := lo.Values[string, int](map[string]int{"foo": 1, "bar": 2})
// []int{1, 2}
英文:

There is a cool lib called lo

> A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)

With this lib you could do many convinient operations like map, filter, reduce and more. Also there are some helpers for map type

Keys

Creates an array of the map keys.

keys := lo.Keys[string, int](map[string]int{&quot;foo&quot;: 1, &quot;bar&quot;: 2})
// []string{&quot;bar&quot;, &quot;foo&quot;}

Values

Creates an array of the map values.

values := lo.Values[string, int](map[string]int{&quot;foo&quot;: 1, &quot;bar&quot;: 2})
// []int{1, 2}

答案10

得分: 1

假设map的类型是map[int]string,你可以使用标准库中的实验性maps包来获取键和值:

package main

import (
	"fmt"
	"golang.org/x/exp/maps"
)

func main() {
	mymap := map[int]string{1: "foo", 2: "bar", 3: "biz"}

	fmt.Println(maps.Keys(mymap))
	fmt.Println(maps.Values(mymap))
}

输出结果:

[2 3 1]
[bar biz foo]
英文:

Assuming map is of type map[int]string, you could get keys and values using the experimental maps package from the standard library:

package main

import (
	&quot;fmt&quot;
	&quot;golang.org/x/exp/maps&quot;
)


func main() {
	mymap := map[int]string{1: &quot;foo&quot;, 2: &quot;bar&quot;, 3: &quot;biz&quot;}

	fmt.Println(maps.Keys(mymap))
	fmt.Println(maps.Values(mymap))
}

Output:

[2 3 1]
[bar biz foo]

huangapple
  • 本文由 发表于 2014年1月26日 19:48:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/21362950.html
匿名

发表评论

匿名网友

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

确定