As you may know, it’s not possible to order a map in Go (just like in any language where maps are implemented as hash maps). However, we can achieve the same result by working with the map’s keys.
The approach is to extract the keys into a slice, sort them, and then retrieve the corresponding values from the map using the sorted keys.
Let’s take a common example: given a list of strings, return the list of strings sorted in decreasing order of their frequency.
func orderByFrequency(slice []string) []string {
occurrenceMap := make(map[string]int, len(slice))
for _, elem := range slice {
occurrenceMap[elem]++
}
keys := make([]string, 0, len(occurrenceMap))
for key := range occurrenceMap {
keys = append(keys, key)
}
sort.SliceStable(keys, func(i, j int) bool {
return occurrenceMap[keys[i]] > occurrenceMap[keys[j]]
})
return keys
}In this approach, we first create a map to count the occurrences, then create a slice of the keys and sort them.
But what happens if we want to add another sorting criterion? For example, if two strings have the same occurrence count, we want to sort them in decreasing order by their string value.
In this case, we modify the sorting function as follows:
sort.SliceStable(keys, func(i, j int) bool {
if occurrenceMap[keys[i]] > occurrenceMap[keys[j]] {
return true
}
if occurrenceMap[keys[i]] == occurrenceMap[keys[j]] && keys[i] > keys[j] {
return true
}
return false
})Using the generics we can create a version for every Comparable:
func orderByFrequency[T comparable](slice []T) []T {
occurrenceMap := make(map[T]int, len(slice))
for _, elem := range slice {
occurrenceMap[elem]++
}
keys := make([]T, 0, len(occurrenceMap))
for key := range occurrenceMap {
keys = append(keys, key)
}
slices.SortFunc(keys, func(a, b T) int {
return cmp.Compare(occurrenceMap[b], occurrenceMap[a])
})
return keys
}Try to create an exercise yourself.