如何获取地图的键

时间:2016-08-22 03:57:12

标签: generics go go-reflect

我有一个名为Keys()的函数来获取地图的所有键,这里是代码:

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
}

但我得到了

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

Go支持泛型,我该如何修复代码?

3 个答案:

答案 0 :(得分:6)

1- Golang是强类型语言,因此map[int]interface{}map[interface{}]interface{}不兼容。
int的类型与interface{}不同, 并参见:Go: What's the meaning of interface{}?

2-不,Golang不支持泛型,这非常好,因为它使语言变得简单快捷。

你有一些选择:

如果您不想更改所用地图的类型:
1-您可以将函数编辑为:func Keys(m map[int]interface{}) []int,如下工作示例代码:

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
}

输出(可能不是有序):

[2 3]

2-或者你可以将函数编辑为:func Keys(m map[int]interface{}) []interface{},就像这个工作示例代码一样:

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
}

输出(可能不是有序):

[2 3]

如果您不想更改使用的Keys功能:
3-您可以将地图编辑为:map[interface{}]interface{},如下工作示例代码:

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-此外,您可以在某些用例中使用reflect包,但会受到性能(速度)的影响 请参阅:The Laws of Reflection

答案 1 :(得分:2)

除了Amd的解决方案,如果您不想更改使用的地图类型,也可以使用反射库。

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

}

请注意,如果您使用此解决方案,则Keys返回的密钥将包含接口本身包含的每个密钥值。因此,要获得实际值,您可能需要键入断言:

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

Working Code

答案 2 :(得分:1)

从 Go 1.18(2022 年初,除非出现延迟)开始,该语言最终将实现类型参数,您将能够轻松编写这样的函数:

package main

import (
    "fmt"
    "reflect"
)

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

    keys := Keys(m2)
    fmt.Println(keys)                 // [2 3]
    fmt.Println(reflect.TypeOf(keys)) // []int
}
func Keys[K comparable](m map[K]interface{}) (keys []K) {
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}

注意,基于current proposal,类型参数K上的类型约束是预先声明的标识符comparable1,而不是{{1 }}。

这是因为映射键必须支持comparison operators

<块引用>

必须为键类型的操作数完整定义比较运算符 any==

因此您必须将 != 限制为仅可比较的类型。


1:关于引入哪些预先声明的标识符仍在讨论中;类型参数实现完成后再回来查看