CodeQuestor
CodeQuestor

Reputation: 911

Sort on map on value (attribute of Struct)

I have the below map:

detail := make(map[string]*Log)

type Log struct {
    Id      []string
    Name    []string
    Priority   int   // value could be 1, 2, 3
    Message    string
}

I want to sort the "detail" map on basis of the value which is a struct in my case. This should be sorted by attribute "Priority".

For example, Log (map of struct) may have values similar to below:

Z : &{[ba60] [XYZ] 3 "I am the boss"}
B : &{[ca50] [ABC] 2 "I am the Junior"}
U : &{[zc20] [PQR] 1 "I am the Newbie"}

I want them to print from increasing Priority order i.e. 1 to 3

U : &{[zc20] [PQR] 1 "I am the Newbie"}
B : &{[ca50] [ABC] 2 "I am the Junior"}
Z : &{[ba60] [XYZ] 3 "I am the boss"}

I tried to use the sort and implemented the Sort interface, but seems like still missing the clue somewhere. So, I implemented the below interface:

type byPriority []*Log

func (d byPriority) Len() int {
    return len(d)
}
func (d byPriority) Less(i, j int) bool {
    return d[i].Priority < d[j].Priority
}
func (d byPriority) Swap(i, j int) {
    d[i], d[j] = d[j], d[i]
}

But how should I apply sort.Sort() method on this map to get the sorted result. Do I need to add some more code?

Upvotes: 1

Views: 3800

Answers (1)

dtolnay
dtolnay

Reputation: 11033

The map type in Go is unordered. Regardless of what you do to a map, the next time you iterate over it you will receive the keys in random order. Thus there is no way to "sort" a map.

What you can do is copy the entries of the map into a slice, which is sortable.

package main

import (
    "fmt"
    "sort"
)

type Log struct {
    Id       []string
    Name     []string
    Priority int // value could be 1, 2, 3
    Message  string
}

type Entry struct {
    key   string
    value *Log
}

type byPriority []Entry

func (d byPriority) Len() int {
    return len(d)
}
func (d byPriority) Less(i, j int) bool {
    return d[i].value.Priority < d[j].value.Priority
}
func (d byPriority) Swap(i, j int) {
    d[i], d[j] = d[j], d[i]
}

func printSorted(detail map[string]*Log) {
    // Copy entries into a slice.
    slice := make(byPriority, 0, len(detail))
    for key, value := range detail {
        slice = append(slice, Entry{key, value})
    }

    // Sort the slice.
    sort.Sort(slice)

    // Iterate and print the entries in sorted order.
    for _, entry := range slice {
        fmt.Printf("%s : %v\n", entry.key, entry.value)
    }
}

func main() {
    detail := map[string]*Log{
        "Z": &Log{[]string{"ba60"}, []string{"XYZ"}, 3, "I am the boss"},
        "B": &Log{[]string{"ca50"}, []string{"ABC"}, 2, "I am the Junior"},
        "U": &Log{[]string{"zc20"}, []string{"PQR"}, 1, "I am the Newbie"},
    }

    printSorted(detail)
}

Upvotes: 3

Related Questions