Golang中如何按值对Map进行排序

Golang中如何按值对Map进行排序 我在Stackoverflow上找到了这个五年前的答案。

这仍然是目前最好的方法吗?还是现在有更好的(所谓更好,我指的是需要更少代码的替代方案)方式来实现这个?

package main

import (
    "fmt"
    "sort"
)

func main() {
    m := map[string]int{
        "something": 10,
        "yo":        20,
        "blah":      20,
    }

    type kv struct {
        Key   string
        Value int
    }

    var ss []kv
    for k, v := range m {
        ss = append(ss, kv{k, v})
    }

    sort.Slice(ss, func(i, j int) bool {
        return ss[i].Value > ss[j].Value
    })

    for _, kv := range ss {
        fmt.Printf("%s, %d\n", kv.Key, kv.Value)
    }
}

来源:sorting - How can I sort a Map[string]int by its values? - Stack Overflow


更多关于Golang中如何按值对Map进行排序的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

@guettli,

据我所知,没有比这更短的代码了。

你想省略什么呢?

更多关于Golang中如何按值对Map进行排序的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我想省略 struct

我发现了一种方法,可以避免使用结构体:排序 - 如何按值对 Map[string]int 进行排序? - Stack Overflow

确实,这个版本大约缩短了20%。

在此发布Stack Overflow上的解决方案以供参考:

func main() {
    counts := map[string]int{"hello": 10, "foo": 20, "bar": 20}

    keys := make([]string, 0, len(counts))
    for key := range counts {
        keys = append(keys, key)
    }
    sort.Slice(keys, func(i, j int) bool { return counts[keys[i]] > counts[keys[j]] })

    for _, key := range keys {
        fmt.Printf("%s, %d\n", key, counts[key])
    }
}

在Go中按值对Map排序,目前仍然推荐使用你找到的方法。虽然Go语言本身没有内置Map排序功能,但这种方法通过将键值对转换为切片再进行排序,是标准且高效的做法。

以下是当前Go 1.21中的实现示例:

package main

import (
    "fmt"
    "slices"
)

func main() {
    m := map[string]int{
        "something": 10,
        "yo":        20,
        "blah":      20,
        "test":      15,
    }

    // 使用泛型切片和slices.SortFunc(Go 1.21+)
    type Pair struct {
        Key   string
        Value int
    }
    
    pairs := make([]Pair, 0, len(m))
    for k, v := range m {
        pairs = append(pairs, Pair{k, v})
    }

    // 使用slices.SortFunc按值降序排序
    slices.SortFunc(pairs, func(a, b Pair) int {
        if a.Value > b.Value {
            return -1 // 降序
        }
        if a.Value < b.Value {
            return 1
        }
        return 0
    })

    // 或者按值升序排序
    slices.SortFunc(pairs, func(a, b Pair) int {
        return a.Value - b.Value // 升序
    })

    for _, p := range pairs {
        fmt.Printf("%s: %d\n", p.Key, p.Value)
    }
}

如果你需要保持与旧版本Go的兼容性,可以使用传统的sort.Slice方法:

package main

import (
    "fmt"
    "sort"
)

func main() {
    m := map[string]int{
        "something": 10,
        "yo":        20,
        "blah":      20,
    }

    // 更简洁的结构体定义
    pairs := make([]struct {
        Key   string
        Value int
    }, 0, len(m))

    for k, v := range m {
        pairs = append(pairs, struct {
            Key   string
            Value int
        }{k, v})
    }

    // 按值降序排序
    sort.Slice(pairs, func(i, j int) bool {
        if pairs[i].Value == pairs[j].Value {
            return pairs[i].Key < pairs[j].Key // 值相同时按键排序
        }
        return pairs[i].Value > pairs[j].Value
    })

    for _, p := range pairs {
        fmt.Printf("%s: %d\n", p.Key, p.Value)
    }
}

对于需要频繁排序的场景,可以考虑封装为通用函数:

func SortMapByValue[K comparable, V constraints.Ordered](m map[K]V, descending bool) []struct {
    Key   K
    Value V
} {
    pairs := make([]struct {
        Key   K
        Value V
    }, 0, len(m))
    
    for k, v := range m {
        pairs = append(pairs, struct {
            Key   K
            Value V
        }{k, v})
    }
    
    sort.Slice(pairs, func(i, j int) bool {
        if descending {
            return pairs[i].Value > pairs[j].Value
        }
        return pairs[i].Value < pairs[j].Value
    })
    
    return pairs
}

这些方法在性能上都是最优的,时间复杂度为O(n log n),其中n是Map的大小。

回到顶部