【问题标题】:How to get sorted list of map keys in Go?如何在 Go 中获取地图键的排序列表?
【发布时间】:2016-05-23 19:35:50
【问题描述】:

假设我有一张地图: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] 的通用函数

【问题讨论】:

    标签: list sorting dictionary go


    【解决方案1】:

    由于 map[string]boolmap[string]stringmap[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
    }
    

    【讨论】:

      【解决方案2】:

      这是我的 0.02 美元。由于键提取逻辑不太可能改变,并且您希望将所有内容保存在一个地方,您可以创建变体并从中选择非零映射:

      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
      }
      

      当然,您可以通过添加更多条件来避免 goto 语句,但我个人觉得它更清晰。

      然后你可以使用如下函数:

      SortedMapKeys(MapVariant{
          Bool: map[string]bool{"a": true, "b": false}
      })
      SortedMapKeys(MapVariant{
          String: map[string]string{"c": "v1", "b": "v2"}
      })
      

      【讨论】:

      • 感谢您的回答,但我不确定这是解决任务的惯用方式。仍然。由于我重复了收集密钥的逻辑(即使它在同一个方法中),我将不得不编写单元测试来检查每个重复的代码片段。如果有一天我想要支持 5 种类型的地图怎么办? 10? 20?如果这段代码将被组织为一个库并且程序员想要收集 map[string]SomeCustomStruct 的键怎么办?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-25
      • 2022-08-16
      • 2021-09-13
      • 2022-01-06
      • 2017-04-02
      • 1970-01-01
      • 2015-08-13
      相关资源
      最近更新 更多