golang's solution for sorting custom types

  • 2020-06-12 09:17:52
  • OfStack

preface

The Go language allows us to customize types, and in real projects, we often need to sort by a field of a struct type. I didn't know how to solve this problem before. Later, I searched related problems on the Internet and found some good solutions. Please refer to here and make a summary.

Since the golang sort package itself provides the functionality, there is no need to build a wheel, so let's see how to use the sort package.

sort package, discussion

The sort package of sorting algorithm is also implemented in golang. The sort package internally implements four basic sorting algorithms: insertion sort (insertionSort), merge sort (symMerge), heap sort (heapSort) and quick sort (quickSort). The sort package automatically selects the best sorting algorithm based on actual data.

So we only need to think about the implementation when we write code sort.Interface This type will do.

Take a quick look at the sort package


func Sort(data Interface) {
 // Switch to heapsort if depth of 2*ceil(lg(n+1)) is reached.
 n := data.Len()
 maxDepth := 0
 for i := n; i > 0; i >>= 1 {
 maxDepth++
 }
 maxDepth *= 2
 quickSort(data, 0, n, maxDepth)
}
type Interface interface {
 // Len is the number of elements in the collection.
 Len() int
 // Less reports whether the element with
 // index i should sort before the element with index j.
 Less(i, j int) bool
 // Swap swaps the elements with indexes i and j.
 Swap(i, j int)
}
//  Internally implemented 4 Sorting algorithm 
//  Insertion sort 
func insertionSort(data Interface, a, b int)
//  Heap sort 
func heapSort(data Interface, a, b int)
//  Quick sort 
func quickSort(data Interface, a, b, maxDepth int)
//  Merge sort 
func symMerge(data Interface, a, m, b int)

So I'm gonna call sort.Sort() To implement custom type sorting, we only need our type to implement the three methods in the Interface interface type.

Take a look at the sort package itself for example []int How types are sorted


//  First of all, I've defined 1 a []int Alias of a type IntSlice 
type IntSlice []int
//  Access to this  slice  The length of the 
func (p IntSlice) Len() int   { return len(p) }
//  Compare the two element sizes   ascending 
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
//  Exchange data 
func (p IntSlice) Swap(i, j int)  { p[i], p[j] = p[j], p[i] }
// sort.Ints() Internal calls Sort()  Method implementation ordering 
//  Pay attention to   The first []int  convert  IntSlice type   Because of this type is implemented Interface the 3 A method of  
func Ints(a []int) { Sort(IntSlice(a)) }

Let's follow suit and sort our custom structure types in descending order


package main
import (
 "fmt"
 "sort"
)
type Person struct {
 Name string
 Age int
}
type Persons []Person
//  Access to this  slice  The length of the 
func (p Persons) Len() int { return len(p) }
//  Sort the elements in descending order by age   (Follow your own business logic here)  
func (p Persons) Less(i, j int) bool {
 return p[i].Age > p[j].Age
}
//  Exchange data 
func (p Persons) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func main() {
 persons := Persons{
 {
 Name: "test1",
 Age: 20,
 },
 {
 Name: "test2",
 Age: 22,
 },
 {
 Name: "test3",
 Age: 21,
 },
 }
 fmt.Println(" Before ordering ")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
 sort.Sort(persons)
 fmt.Println(" After ordering ")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
}

Actually, one is the same Len() and Swap() Basically nothing is changed, only the comparison of elements is involved Less() The approach will change.

What do we do when we sort more than one field in a structure? Do we write these three methods for every one sorted? Of course not. We can solve this problem with nested constructs. Because a nested structure can inherit all the properties and methods of a parent structure

For example, if I want to sort the Name field and Age pair of Person above, we can use nested constructs to improve 1.


package main
import (
 "fmt"
 "sort"
)
type Person struct {
 Name string
 Age int
}
type Persons []Person
// Len() Methods and Swap() The method does not change 
//  Access to this  slice  The length of the 
func (p Persons) Len() int { return len(p) }
//  Exchange data 
func (p Persons) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
//  Nested structures   Will inherit  Person  All properties and methods 
//  So it's equivalent to SortByName  Also achieved  Len()  and  Swap()  methods 
type SortByName struct{ Persons }
//  Sort by the name length of the element in descending order   (Follow your own business logic here) 
func (p SortByName) Less(i, j int) bool {
 return len(p.Persons[i].Name) > len(p.Persons[j].Name)
}
type SortByAge struct{ Persons }
//  Sort the elements in descending order by age   (Follow your own business logic here) 
func (p SortByAge) Less(i, j int) bool {
 return p.Persons[i].Age > p.Persons[j].Age
}
func main() {
 persons := Persons{
 {
 Name: "test123",
 Age: 20,
 },
 {
 Name: "test1",
 Age: 22,
 },
 {
 Name: "test12",
 Age: 21,
 },
 }
 fmt.Println(" Before ordering ")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
 sort.Sort(SortByName{persons})
 fmt.Println(" After ordering ")
 for _, person := range persons {
 fmt.Println(person.Name, ":", person.Age)
 }
}

conclusion


Related articles: