304
votes

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++
}
6
The correct answer is no, there is not a simpler/nicer way.dldnh

6 Answers

251
votes

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.

439
votes

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.

97
votes

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

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]
}
17
votes

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

14
votes

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.

2
votes

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