How to get keys of map

12,052

Solution 1

1- Golang is strongly typed language, So the map[int]interface{} is not compatible with map[interface{}]interface{}.
int is different type than interface{}, and see: Go: What's the meaning of interface{}?

2- No, Golang doesn't support generics, and this is very good, because it makes language simple and fast.


You have some options:

If you don't want to change the type of map used:
1- You may edit the function to: func Keys(m map[int]interface{}) []int, like this working sample code:

package main

import "fmt"

func main() {
    m2 := map[int]interface{}{
        2: "string",
        3: "int",
    }
    fmt.Println(Keys(m2))
}

func Keys(m map[int]interface{}) []int {
    keys := make([]int, len(m))
    i := 0
    for k := range m {
        keys[i] = k
        i++
    }
    return keys
}

output ( may not be in order):

[2 3]

2- Or you may edit the function to: func Keys(m map[int]interface{}) []interface{}, like this working sample code:

package main

import "fmt"

func main() {
    m2 := map[int]interface{}{
        2: "string",
        3: "int",
    }
    fmt.Println(Keys(m2))
}

func Keys(m map[int]interface{}) []interface{} {
    keys := make([]interface{}, len(m))
    i := 0
    for k := range m {
        keys[i] = k
        i++
    }
    return keys
}

output ( may not be in order):

[2 3]

If you don't want to change the Keys function used:
3- You may edit the map to: map[interface{}]interface{}, like this working sample code:

package main

import "fmt"

func main() {
    m2 := map[interface{}]interface{}{
        2: "string",
        3: "int",
    }
    fmt.Println(Keys(m2))
}

func Keys(m map[interface{}]interface{}) []interface{} {
    keys := make([]interface{}, len(m))
    i := 0
    for k := range m {
        keys[i] = k
        i++
    }
    return keys
}

4- Also you may use reflect package for some use cases, but with the performance (speed) penalty.
And See: The Laws of Reflection

Solution 2

Besides Amd's solution, you can also make use of the reflect library if you do not want to change the type of map used.

func main() {
    m2 := map[int]interface{}{
        2: "string",
        3: "int",
    }

    k := Keys(m2)

    fmt.Printf("Keys: %v\n", k)
}

func Keys(m interface{}) (keys []interface{}) {
    v := reflect.ValueOf(m)
    if v.Kind() != reflect.Map {
        fmt.Errorf("input type not a map: %v", v)
    }

    for _, k := range v.MapKeys() {
        keys = append(keys, k.Interface())
    }
    return keys

}

Note that if you use this solution, the returned keys from Keys will contain each key value wrapped in an interface itself. So to get the actual value you might have to do type assertions:

k := Keys(m2)
k1 := k[0].(int) // k[0] is an interface value, k1 is an int

Working Code.

Solution 3

Starting from Go 1.18 (released in beta) the language adds type parameters and you are be able to easily write a function like this:

func Keys[K comparable, V any](m map[K]V) []K {
    keys := make([]K, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}

Example usage:

func main() {
    m := map[int]string{2: "string", 3: "int"}
    keys := Keys(m)
    fmt.Println(keys)                 // [2 3]
    fmt.Println(reflect.TypeOf(keys)) // []int

    m2 := map[string]int{"a": 1, "b": 2}
    keys2 := Keys(m2)
    fmt.Println(keys2)                 // [a b]
    fmt.Println(reflect.TypeOf(keys2)) // []string
}

Playground: https://gotipplay.golang.org/p/pdsI2H7w-N4

Note that, based on the current proposal, the type constraint on the type parameter K is the predeclared identifier comparable, instead of any.

This is because map keys must support comparison operators. Therefore you must restrict K to comparable types only.


Alternatively based on this accepted proposal the new package maps is also available to accomplish the same thing. However this is not yet in the standard library. Instead it was included in golang.org/x/exp, therefore it is not covered by Go 1 compatibility promise.

The difference between maps.Keys and the function above is that maps.Keys is parametrized on M (other than K and V), with the approximate constraint ~map[K]V. This allows all defined types with underlying map:

type MyMap map[string]int

The usage is basically the same:

package main

import (
    "fmt"
    "reflect"

    "golang.org/x/exp/maps"
)

func main() {
    m := map[int]string{2: "string", 3: "int"}
    keys := maps.Keys(m)
    fmt.Println(keys)                 // [2 3]
    fmt.Println(reflect.TypeOf(keys)) // []int
}

Playground: https://gotipplay.golang.org/p/Bx11jmyifAg

Share:
12,052
GoingMyWay
Author by

GoingMyWay

A Self-Learner!

Updated on August 15, 2022

Comments

  • GoingMyWay
    GoingMyWay almost 2 years

    I have a function named Keys() to get all the keys of a map, here is the code:

    func main() {
        m2 := map[int]interface{}{
            2:"string",
            3:"int",
        }
        fmt.Println(Keys(m2))
    }
    func Keys(m map[interface{}]interface{}) (keys []interface{}) {
        for k := range m {
            keys = append(keys, k)
        }
        return keys
    }
    

    But I got

    cannot use m2 (type map[int]interface {}) as type map[interface {}]interface {} in argument to Keys
    

    Does Go support generics and how should I fix my code?