让我们想象一下我有一张地图:map[string]string
。我想获取此地图的排序键列表。所以我可以这样做:
func SortedMapKeys(m map[string]string) (keyList []string) {
for key := range m {
keyList = append(keyList, key)
}
sort.Strings(keyList)
return
}
然后我会有另一张map[string]bool
类型的地图。我也想得到它的钥匙。但问题是函数SortedMapKeys接受map[string]string
参数。所以我需要编写完全相同的函数,只有一个区别 - 它会接受map[string]bool
。
由于显而易见的原因,这不是一种选择。如果有一天我想改变我获取和排序键的逻辑,我将需要跟踪和更新所有这些功能。此外,我将不得不为所有这些函数编写相同的单元测试,这些函数实际上做同样的事情,因为它们的主体是100%相等的(代码重复)。
有没有办法创建一个可以接受任何的map[string]
的通用函数?
答案 0 :(得分:5)
由于map[string]bool
和map[string]string
以及map[string]Whatever
都是不同的类型,因此创建单个函数来对所有可能的map[string]*
类型的键进行排序的唯一方法是通过反射
func SortedMapKeys(m interface{}) (keyList []string) {
keys := reflect.ValueOf(m).MapKeys()
for _, key := range keys {
keyList = append(keyList, key.Interface().(string))
}
sort.Strings(keyList)
return
}
对于中间解决方案,因为您可能只关注几种类型的组合,您可以使用类型开关来提取密钥
func SortedMapKeys(m interface{}) (keyList []string) {
switch m := m.(type) {
case map[string]string:
for k := range m {
keyList = append(keyList, k)
}
case map[string]bool:
for k := range m {
keyList = append(keyList, k)
}
default:
panic("unknown map type")
}
sort.Strings(keyList)
return
}
答案 1 :(得分:1)
Here's my 0.02$. Since keys extracting logic is unlikely to change and you want to keep everything in one place you can create variant and choose non-nil map from it:
type MapVariant struct {
Bool map[string]bool
String map[string]string
}
func SortedMapKeys(variant MapVariant) (keyList []string) {
if variant.String != nil {
for k := range variant.String {
keyList = append(keyList, k)
}
goto SORT
}
if variant.Bool != nil {
for k := range variant.Bool {
keyList = append(keyList, k)
}
goto SORT
}
SORT:
sort.Strings(keyList)
return
}
Of course you can avoid goto statements by adding more conditions but I personally find it clearer.
Then you can use the function like:
SortedMapKeys(MapVariant{
Bool: map[string]bool{"a": true, "b": false}
})
SortedMapKeys(MapVariant{
String: map[string]string{"c": "v1", "b": "v2"}
})