I have an array/slice of members:
type Member struct {
Id int
LastName string
FirstName string
}
var members []Member
My quest
Use the sort.Slice (available since Go 1.8) or the sort.Sort function to sort a slice of values.
With both functions, the application provides a function that tests if one slice element is less than another slice element. To sort by last name and then first name, compare last name and then first name:
if members[i].LastName < members[j].LastName {
return true
}
if members[i].LastName > members[j].LastName {
return false
}
return members[i].FirstName < members[j].FirstName
The less function is specified using an anonymous function with sort.Slice:
var members []Member
sort.Slice(members, func(i, j int) bool {
if members[i].LastName < members[j].LastName {
return true
}
if members[i].LastName > members[j].LastName {
return false
}
return members[i].FirstName < members[j].FirstName
})
The less function is specified with through an interface with the sort.Sort function:
type byLastFirst []Member
func (members byLastFirst) Len() int { return len(members) }
func (members byLastFirst) Swap(i, j int) { members[i], members[j] = members[j], members[i] }
func (members byLastFirst) Less(i, j int) bool {
if members[i].LastName < members[j].LastName {
return true
}
if members[i].LastName > members[j].LastName {
return false
}
return members[i].FirstName < members[j].FirstName
}
sort.Sort(byLastFirst(members))
Unless performance analysis shows that sorting is a hot spot, use the function that's most convenient for your application.
Use the newer sort.Slice
function as such:
sort.Slice(members, func(i, j int) bool {
switch strings.Compare(members[i].FirstName, members[j].FirstName) {
case -1:
return true
case 1:
return false
}
return members[i].LastName > members[j].LastName
})
or something like that.
All good answers, If you don't want to write any configuration. You can use the external package to perform sorting.
go get -d github.com/raunakjodhawat/multisort
And call the function like so:
sortedSlice, err := multisort.MultiSorted(inputSlice, inputKeys, SortOrders)
To look at a concrete example, go to: https://github.com/raunakjodhawat/multisort
This has been very helpful. I needed to sort a slice of structs, and found my answer here. I actually extended it to triply-sorting. Although sorting this much is not optimal for runtime purposes, it's useful in some circumstances, especially when the alternative leads to code that is hard to maintain or modify and where faster runtimes are not crucial.
sort.Slice(servers, func(i, j int) bool {
if servers[i].code < servers[j].code {
return true
} else if servers[i].code > servers[j].code {
return false
} else {
if servers[i].group < servers[j].group {
return true
} else {
if servers[i].group > servers[j].group {
return false
}
}
}
return servers[i].IDGroup < servers[j]. IDGroup
})
This code sorts first by code, then by group, then by IDGroup.
Here is a slightly more concise implementation of the accepted answer:
package main
import (
"fmt"
"sort"
)
type Member struct {
FirstName string
LastName string
}
func main() {
members := []Member{
{"John", "Doe"},
{"Jane", "Doe"},
{"Mary", "Contrary"},
}
fmt.Println(members)
sort.Slice(members, func(i, j int) bool {
return members[i].LastName < members[j].LastName || members[i].FirstName < members[j].FirstName
})
fmt.Println(members)
}
Running will print the following output:
[{John Doe} {Jane Doe} {Mary Contrary}]
[{Mary Contrary} {Jane Doe} {John Doe}]
This is as expected: the members are printed in order of ascending last name, where in the case of a tie, they are printed in order of first name.
Another pattern, which I find slightly cleaner:
if members[i].LastName != members[j].LastName {
return members[i].LastName < members[j].LastName
}
return members[i].FirstName < members[j].FirstName