如何在Go中获取排序后的映射键列表?

huangapple go评论124阅读模式
英文:

How to get sorted list of map keys in Go?

问题

让我们假设我有一个地图:map[string]string。我想要获取这个地图的按键排序后的列表。所以我可以这样做:

  1. func SortedMapKeys(m map[string]string) (keyList []string) {
  2. for key := range m {
  3. keyList = append(keyList, key)
  4. }
  5. sort.Strings(keyList)
  6. return
  7. }

然后,我将有另一个类型为map[string]bool的地图。我也想要获取它的键。但问题是SortedMapKeys函数只接受map[string]string类型的参数。所以我需要编写完全相同的函数,唯一的区别是它将接受map[string]bool类型的参数。

出于明显的原因,这不是一个选项。如果有一天我想要更改获取和排序键的逻辑,我将需要跟踪和更新所有这些函数。而且,我将不得不为所有这些函数编写相同的单元测试,因为它们的主体完全相同(代码重复)。

有没有办法创建一个通用函数,它可以接受一个map[string] 任意类型的值

英文:

Let's imagine I have a map: map[string]string. I would like to get the list of sorted keys for this map. So I could do something like this:

  1. func SortedMapKeys(m map[string]string) (keyList []string) {
  2. for key := range m {
  3. keyList = append(keyList, key)
  4. }
  5. sort.Strings(keyList)
  6. return
  7. }

Then I will have another map of type map[string]bool. I would like to get it's keys also. But the problem is that function SortedMapKeys accepts a map[string]string argument. So I need to write exactly the same function, with the only one difference - it will accept a map[string]bool.

For obvious reasons it's not an option. If one day I would like to change the logic of how I get and sort my keys, I will need to track and update all these functions. Also, I will have to write the same unit tests for all these functions which actually do the same thing since their bodies are 100% equal (code duplicate).

Is there any way to create a generic function which could accept an map[string] of whatever?

答案1

得分: 6

由于map[string]boolmap[string]stringmap[string]Whatever都是不同的类型,所以唯一的方法是通过反射来创建一个可以对所有可能的map[string]*类型的键进行排序的函数。

  1. func SortedMapKeys(m interface{}) (keyList []string) {
  2. keys := reflect.ValueOf(m).MapKeys()
  3. for _, key := range keys {
  4. keyList = append(keyList, key.Interface().(string))
  5. }
  6. sort.Strings(keyList)
  7. return
  8. }

对于一个中间解决方案,由于你可能只关心几种类型的组合,你可以使用类型切换来提取键。

  1. func SortedMapKeys(m interface{}) (keyList []string) {
  2. switch m := m.(type) {
  3. case map[string]string:
  4. for k := range m {
  5. keyList = append(keyList, k)
  6. }
  7. case map[string]bool:
  8. for k := range m {
  9. keyList = append(keyList, k)
  10. }
  11. default:
  12. panic("unknown map type")
  13. }
  14. sort.Strings(keyList)
  15. return
  16. }
英文:

Since map[string]bool and map[string]string and map[string]Whatever are all distinct types, the only way to create a single function to sort the keys of all possible map[string]* types is via reflection.

  1. func SortedMapKeys(m interface{}) (keyList []string) {
  2. keys := reflect.ValueOf(m).MapKeys()
  3. for _, key := range keys {
  4. keyList = append(keyList, key.Interface().(string))
  5. }
  6. sort.Strings(keyList)
  7. return
  8. }

For an in-between solution, since there are probably only a few combinations of types you're concerned with, you can use a type switch to extract the keys

  1. func SortedMapKeys(m interface{}) (keyList []string) {
  2. switch m := m.(type) {
  3. case map[string]string:
  4. for k := range m {
  5. keyList = append(keyList, k)
  6. }
  7. case map[string]bool:
  8. for k := range m {
  9. keyList = append(keyList, k)
  10. }
  11. default:
  12. panic("unknown map type")
  13. }
  14. sort.Strings(keyList)
  15. return
  16. }

答案2

得分: 1

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

  1. type MapVariant struct {
  2. Bool map[string]bool
  3. String map[string]string
  4. }
  5. func SortedMapKeys(variant MapVariant) (keyList []string) {
  6. if variant.String != nil {
  7. for k := range variant.String {
  8. keyList = append(keyList, k)
  9. }
  10. goto SORT
  11. }
  12. if variant.Bool != nil {
  13. for k := range variant.Bool {
  14. keyList = append(keyList, k)
  15. }
  16. goto SORT
  17. }
  18. SORT:
  19. sort.Strings(keyList)
  20. return
  21. }

当然,您可以通过添加更多条件来避免使用goto语句,但我个人认为这样更清晰。

然后,您可以像这样使用该函数:

  1. SortedMapKeys(MapVariant{
  2. Bool: map[string]bool{"a": true, "b": false}
  3. })
  4. SortedMapKeys(MapVariant{
  5. String: map[string]string{"c": "v1", "b": "v2"}
  6. })
英文:

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:

  1. type MapVariant struct {
  2. Bool map[string]bool
  3. String map[string]string
  4. }
  5. func SortedMapKeys(variant MapVariant) (keyList []string) {
  6. if variant.String != nil {
  7. for k := range variant.String {
  8. keyList = append(keyList, k)
  9. }
  10. goto SORT
  11. }
  12. if variant.Bool != nil {
  13. for k := range variant.Bool {
  14. keyList = append(keyList, k)
  15. }
  16. goto SORT
  17. }
  18. SORT:
  19. sort.Strings(keyList)
  20. return
  21. }

Of course you can avoid goto statements by adding more conditions but I personally find it clearer.

Then you can use the function like:

  1. SortedMapKeys(MapVariant{
  2. Bool: map[string]bool{"a": true, "b": false}
  3. })
  4. SortedMapKeys(MapVariant{
  5. String: map[string]string{"c": "v1", "b": "v2"}
  6. })

huangapple
  • 本文由 发表于 2016年2月12日 23:25:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/35366224.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定