Home > Back-end >  How to sort map by value and if value equals then by key in Go?
How to sort map by value and if value equals then by key in Go?

Time:11-30

I came across a situation where I want to sort a map first by value and if the values are equal then by key in GO. Here is an example of input and expected output.

import "fmt"

func main() {
     wordFrequency := map[string]int{"banana": 3, "america": 2, "abb": 2, "test": 2, "car": 1}
     fmt.Printf("%v", wordFrequency)
}

Expected output : {"banana": 3,"abb": 2, "america": 2, "test": 2, "car": 1}

Here banana is at first because its value is 3. abb and america both value is 2 but abb comes first as sorted in ascender order. So my question is how to implement this behaviour where we sort the entries in map first by value and if value equals we sort them by key. I have checked related documentation and previous questions but none directly address this scenario. If it is not possible to do due to map data structure in Go, how can we do that efficiently with other structures?

In Python 3, we can do this in the following way

>>> d = {'apple':2, 'banana':3, 'almond':2, 'beetroot':3, 'peach':4}
>>> [k for k, v in sorted(d.iteritems(), key=lambda(k, v): (-v, k))]
['peach', 'banana', 'beetroot', 'almond', 'apple']

Here is a link for the equivalent question for Python link

N.B: This question asks how to sort map by value. My question is specific to sort by value and then key (if value equals).

CodePudding user response:

You can't sort a map.

  1. Convert the map to a slice of key-value-pair structs.
  2. Sort the slice (see also How to sort struct with multiple sort parameters?)

For example like this:

func main() {
    wordFrequency := map[string]int{"banana": 3, "america": 2, "abb": 2, "test": 2, "car": 1}

    vec := mapToSlice(wordFrequency)

    sort.Slice(vec, func(i, j int) bool {
        // 1. value is different - sort by value (in reverse order)
        if vec[i].value != vec[j].value {
            return vec[i].value > vec[j].value
        }
        // 2. only when value is the same - sort by key
        return vec[i].key < vec[j].key
    })

    fmt.Printf("%v", vec)
}

func mapToSlice(in map[string]int) []KV {
    vec := make([]KV, len(in))
    i := 0
    for k, v := range in {
        vec[i].key = k
        vec[i].value = v
        i  
    }
    return vec
}

type KV struct {
    key   string
    value int
}

Prints:

[{banana 3} {abb 2} {america 2} {test 2} {car 1}]

Live demo

  • Related