How can I sort a Map[string]int by its values?

SortingDictionaryGo

Sorting Problem Overview


Given this code block

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

I would like to print out

foo, 20
bar, 20
hello, 10

In the order of highest to lowest

Sorting Solutions


Solution 1 - Sorting

Found the answer on Golang-nuts by Andrew Gerrand

You can implement the sort interface by writing the len/less/swap functions

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

For the original post, please find it here https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw

Solution 2 - Sorting

There's a new sort.Slice function in go 1.8, so now this is simpler.

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

Solution 3 - Sorting

For example:

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

Playground


Output:

foo, 20
bar, 20
hello, 10

Solution 4 - Sorting

Sort keys first by value and then iterate map:

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

Solution 5 - Sorting

I often need to sort a map[string]int of something I’m counting and have been using the following.

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
}

Use it to iterate over the keys in order of value

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

Solution 6 - Sorting

In my case, I was dealing with a program that I created. In this program, I created a Map just like you, with string and int. Then I discovered like you that Go doesn't really have a built-in way to sort something like this. I read the other answers and didn't really like what I read.

So I tried to think about the problem differently. Go can use sort.Ints with a slice. Also, Go can use sort.Slice with a custom comparator. So instead of creating a Map of string and int, I created a struct of string and int. Then you can sort:

package main

import (
   "fmt"
   "sort"
)

type file struct {
   name string
   size int
}

func main() {
   a := []file{
      {"april.txt", 9}, {"may.txt", 7},
   }
   sort.Slice(a, func (d, e int) bool {
      return a[d].size < a[e].size
   })
   fmt.Println(a)
}

This will not work for everyone, because maybe you will be forced to deal with a map someone else created. But it was useful for me. Good part is, unlike all the other answers, this one uses no loops.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsamolView Question on Stackoverflow
Solution 1 - SortingsamolView Answer on Stackoverflow
Solution 2 - SortingvoutasaurusView Answer on Stackoverflow
Solution 3 - SortingzzzzView Answer on Stackoverflow
Solution 4 - SortingAlexanderYastrebovView Answer on Stackoverflow
Solution 5 - SortingXeoncrossView Answer on Stackoverflow
Solution 6 - SortingZomboView Answer on Stackoverflow