Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a slice of keys from a map

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++
}
like image 923
Saswat Padhi Avatar asked Jan 26 '14 11:01

Saswat Padhi


4 Answers

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.

like image 69
Vinay Pai Avatar answered Nov 20 '22 15:11

Vinay Pai


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.

like image 44
peterSO Avatar answered Nov 20 '22 14:11

peterSO


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]
}
like image 118
Denis Kreshikhin Avatar answered Nov 20 '22 16:11

Denis Kreshikhin


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{"alpha": 1, "bravo": 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 Go 1.19 the future.

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

For those who don't like to import exp packages, you can copy the source code:

// 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
}
like image 42
blackgreen Avatar answered Nov 20 '22 16:11

blackgreen