如何通过Map [string] int的值对其排序?


80

给定此代码块

map[string]int {"hello":10, "foo":20, "bar":20}

我想打印出来

foo, 20
bar, 20
hello, 10

从高到低的顺序

谢谢!

Answers:


91

在Andrew Gerrand的Golang-nuts上找到了答案

您可以通过编写len / less / swap函数来实现sort接口

func rankByWordCount(wordFrequencies map[string]int) PairList{
  pl := make(PairList, len(wordFrequencies))
  i := 0
  for k, v := range wordFrequencies {
    pl[i] = Pair{k, v}
    i++
  }
  sort.Sort(sort.Reverse(pl))
  return pl
}

type Pair struct {
  Key string
  Value int
}

type PairList []Pair

func (p PairList) Len() int { return len(p) }
func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value }
func (p PairList) Swap(i, j int){ p[i], p[j] = p[j], p[i] }

对于原始帖子,请在这里https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw找到


1
...除了Less返回错误的结果。对于反向排序,请使用>
Fred Foo 2013年

3
@larsmans我不好!感谢您指出。我改用sort.Reverse获得相反的结果
samol 2013年

2
更好的是,我什至不知道sort.Reverse。+1。
Fred Foo 2013年

71

Go 1.8中有一个新的sort.Slice函数,所以现在更简单了。

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)
    }
}

https://play.golang.org/p/y1_WBENH4N


我不喜欢输出的结果不是我开始的地图
hendry

@hendry这个答案专门针对原始问题的格式。在go1.12中,您只需打印地图即可进行排序,请参见相应的问题:github.com/golang/go/issues/21095
voutasaurus

几乎完美,我建议处理具有相同值的元素。
Tommaso Barbugli

@TommasoBarbugli怎么样?使它们稳定?还是按字母顺序排序?绝对不可能确定稳定性,因为Go专门将映射迭代顺序随机化,以阻止您依赖显式调用的对编译器不重要的顺序。对于字母就可以很容易地修改传递给sort.Slice匿名函数
voutasaurus

1
还有一个sort.SliceStable(在Go 1.8中也添加了),它保留了equal元素的原始顺序。
戴夫·雅伍德

16

例如:

package main

import (
        "fmt"
        "sort"
)

func main() {
        m := map[string]int{"hello": 10, "foo": 20, "bar": 20}
        n := map[int][]string{}
        var a []int
        for k, v := range m {
                n[v] = append(n[v], k)
        }
        for k := range n {
                a = append(a, k)
        }
        sort.Sort(sort.Reverse(sort.IntSlice(a)))
        for _, k := range a {
                for _, s := range n[k] {
                        fmt.Printf("%s, %d\n", s, k)
                }
        }
}

操场


输出:

foo, 20
bar, 20
hello, 10

@DarshanComputing:谢谢,已修复。
zzzz 2013年

1
假设这些值没有标识。
newacct

2
@newacct:它仅解决OP问题,而不解决一般情况;-)
zzzz 2013年

这个解决方案也适用于我的情况,简单易懂。
汤米

1

我经常需要对map[string]int我正在计数的东西进行排序,并且一直在使用以下内容。

func rankMapStringInt(values map[string]int) []string {
    type kv struct {
        Key   string
        Value int
    }
    var ss []kv
    for k, v := range values {
        ss = append(ss, kv{k, v})
    }
    sort.Slice(ss, func(i, j int) bool {
        return ss[i].Value > ss[j].Value
    })
    ranked := make([]string, len(values))
    for i, kv := range ss {
        ranked[i] = kv.Key
    }
    return ranked
}

用它按值顺序遍历键

values := map[string]int{"foo": 10, "bar": 20, "baz": 1}

for i, index := range rankMapStringInt(values) {
    fmt.Printf("%3d: %s -> %d", i, index, values[index])
}

0

就我而言,我正在处理自己创建的程序。在此程序中,我string和一样使用和创建了一个Map int。然后,我发现像您一样,Go确实没有内置的方式可以对此类内容进行排序。我读了其他答案,但并不真正喜欢我读的书。

因此,我试图以不同的方式考虑问题。Go可以使用 带有切片的sort.Ints。此外,Go可以将sort.Slice与自定义比较器一起使用。因此,而不是创建一个映射的stringint,我创建了一个structstringint。然后您可以排序:

package main

import (
   "fmt"
   "sort"
)

type File struct {
   Name string
   Size int
}

func main() {
   a := []File{{"april.txt", 9}, {"may.txt", 7}}
   f := func (n, n1 int) bool {
      return a[n].Size < a[n1].Size
   }
   sort.Slice(a, f)
   fmt.Println(a)
}

这并非对所有人都适用,因为也许您将不得不处理别人创建的地图。但这对我很有用。好的部分是,与所有其他答案不同,该答案没有使用循环。


-2

首先按值对键进行排序,然后对映射进行迭代:

package main

import (
    "fmt"
    "sort"
)

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])
    }
}

我很确定谁在投票反对,也许是那些不仔细阅读比较器功能的人。也可以接受的答案不会产生要求的打印输出OP,而是引入了必须以实际代码维护的新数据结构。这是我的答案的游乐场链接play.golang.org/p/Y4lrEm2-hT5
AlexanderYastrebov
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.