2
votes

I would like to get the keys of an arbitrary map variable. And I found this function, which replicates the PHP array_keys behaviour: https://www.php2golang.com/method/function.array-keys.html. But the following code:

items := map[string]int{
    "one":   1,
    "two":   2,
    "three": 3,
}
keys := ArrayKeys(items)

throws the next 'compile time' exception:

cannot use items (type map[string]int) as type map[interface {}]interface {} in argument to ArrayKeys

what am I doing wrong?

Here's an example: https://play.golang.org/p/0ImqjPJFFiE

3
Please post the code where you are using the maps in golang. Show the definition of ArrayKeys function.Himanshu
you will have to specialize it for each type you want to iterate over. One generic solution involves usage of the reflection package, but it is noticeably slower.mh-cbon
about reflection package usage, see stackoverflow.com/a/25773086/4466350mh-cbon

3 Answers

3
votes

You should change the function to work with your types:

func ArrayKeys(elements map[string]int) []string  {
    i, keys := 0, make([]string, len(elements))
    for key, _ := range elements {
        keys[i] = key
        i++
    }
    return keys
}

https://play.golang.org/p/n5kZego0ePY

But why should you change the signature?

If a function in Go takes an empty interface all the including variables has no type any more. But Go is strongly typed, so dealing with variables without a type is difficult. If you provide a function like yours the code should deal really with any type. To do so you would need to use the reflection package. And if you have an error that you missed something your program will panic, when the error occurs. When you writing a server it can run for months and stop running then.

If you write an ArrayKey function for every type you have the things are different. The compiler ensures that your code is robust and will never panic at that point. If you want to try to use the function with another type of map the code will not compile. That is the way Go works.

2
votes

In Go, write a simple function. For example,

package main

import "fmt"

func main() {

    items := map[string]int{
        "one":   1,
        "two":   2,
        "three": 3,
    }

    keys := func(m map[string]int) []string {
        mk := make([]string, 0, len(m))
        for k := range m {
            mk = append(mk, k)
        }
        return mk
    }(items)

    fmt.Printf("%d %q\n", len(keys), keys)
}

Playground: https://play.golang.org/p/B0kOxAGbQCZ

Output:

3 ["three" "one" "two"]

For sorted keys,

package main

import (
    "fmt"
    "sort"
)

func main() {

    items := map[string]int{
        "one":   1,
        "two":   2,
        "three": 3,
    }

    keys := func(m map[string]int) []string {
        mk := make([]string, 0, len(m))
        for k := range m {
            mk = append(mk, k)
        }
        sort.Strings(mk)
        return mk
    }(items)

    fmt.Printf("%d %q\n", len(keys), keys)
}

Playground: https://play.golang.org/p/IQLx7gjGk8j

Output:

3 ["one" "three" "two"]
0
votes

The error clearly mentioned:

cannot use items (type map[string]int) as type map[interface {}]interface {} in argument to ArrayKeys

Because of mismatched types you are getting above error. If you want to use any type in the function you can go for interface because interface{} is not equal to map[interface{}]interface{}

You can go for interface{} to wrap the data coming from maps if you really want to use interface. Else you should use the same type as an argument to the function as mentioned in above answers.

package main

import (
    "fmt"
)

func ArrayKeys(elements interface{}) []interface{} {
    i, keys := 0, make([]interface{}, len(elements.(interface{}).(map[string]int)))
    for key, _ := range elements.(interface{}).(map[string]int) {
        keys[i] = key
        i++
    }
    return keys
}

func main() {
    items := map[string]int{
        "one":   1,
        "two":   2,
        "three": 3,
    }
    keys := ArrayKeys(items)
    fmt.Println(keys)
}

Working code on Go Playground