从地图中获取一部分键

问题描述 投票:0回答:9

有没有更简单/更好的方法从 Go 中的地图中获取一片键?

目前我正在迭代地图并将键复制到切片:

i := 0
keys := make([]int, len(mymap))
for k := range mymap {
    keys[i] = k
    i++
}
dictionary go slice
9个回答
523
投票

这是一个老问题,但这是我的两分钱。 PeterSO 的回答稍微简洁一些,但效率略低。你已经知道它有多大,所以你甚至不需要使用 append:

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

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

在大多数情况下,它可能不会有太大的不同,但它并没有更多的工作,并且在我的测试中(使用具有 1,000,000 个随机

int64
键的地图,然后使用每种方法生成键数组十次) ,直接分配数组成员比使用追加快 20%。

虽然设置容量消除了重新分配,但追加仍然需要做额外的工作来检查是否已达到每个追加的容量。


295
投票

例如,

package main

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

为了在 Go 中高效,最小化内存分配是很重要的。


122
投票

您还可以通过包“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]
}

56
投票

Go 现在有泛型。您可以使用

maps.Keys
获得任何地图的钥匙。

用法示例:

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

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

maps
包在
golang.org/x/exp/maps
中找到。这是实验性的,不在 Go 兼容性保证范围内。他们的目标是将它移动到 Go 1.19 未来的标准库中。

游乐场:https://go.dev/play/p/fkm9PrJYTly

对于那些不喜欢导入 exp 包的人,这里是源代码(原作者为Ian Lance Taylor),如您所见,非常简单:

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

24
投票

我对其他回复中描述的三种方法做了一个粗略的基准。

很明显在拉动键之前预分配切片比

append
ing更快,但令人惊讶的是,
reflect.ValueOf(m).MapKeys()
方法明显比后者慢:

❯ 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

这是代码:https://play.golang.org/p/Z8O6a2jyfTH (在操场上运行它会中止声称它花费的时间太长,所以,好吧,在本地运行它。)


18
投票

更好的方法是使用

append

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

除此之外,你就不走运了——Go 不是一种非常有表现力的语言。


3
投票

Vinay Pai
answer的通用版本(go 1.18+)。

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

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

0
投票

有一个很酷的库叫做

lo

基于 Go 1.18+ Generics (map, filter, contains, find...) 的 Lodash 风格的 Go 库

有了这个库,你可以做很多方便的操作,比如 map、filter、reduce 等等。还有一些

map
类型

的帮手

钥匙

创建地图键数组。

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

价值观

创建地图值数组。

values := lo.Values[string, int](map[string]int{"foo": 1, "bar": 2})
// []int{1, 2}
© www.soinside.com 2019 - 2024. All rights reserved.