ZhangZhihui's Blog  

Problem: You want to sort elements in an array or slice.


Solution: For int , float64 , and string arrays or slices you can use sort.Ints , sort.Float64s , and sort.Strings . You can also use a custom comparator by using sort.Slice . For structs, you can create a sortable interface by implementing the sort.Interface interface and then using sort.Sort to sort the array or slice.

复制代码
package main

import (
    "fmt"
    "sort"
)

func main() {
    integers := []int{3, 14, 159, 26, 53}
    floats := []float64{3.14, 1.41, 1.73, 2.72, 4.53}
    strings := []string{"the", "quick", "brown", "fox", "jumped"}

    sort.Ints(integers)
    fmt.Println(integers)
    sort.Sort(sort.Reverse(sort.IntSlice(integers)))  // sort.IntSlice is a type that implements sort.Interface interface
    fmt.Println(integers)
    sort.Float64s(floats)
    fmt.Println(floats)
    sort.Sort(sort.Reverse(sort.Float64Slice(floats)))  // sort.Float64SLice is a type that implements sort.Interface interface
    fmt.Println(floats)
    sort.Strings(strings)
    fmt.Println(strings)
    sort.Sort(sort.Reverse(sort.StringSlice(strings)))  // sort.StringSlice is a type that implements sort.Interface interface
    fmt.Println(strings)
}
复制代码

 

zzh@ZZHPC:/zdata/Github/mastering-go-expertise$ go run main.go
[3 14 26 53 159]
[159 53 26 14 3]
[1.41 1.73 2.72 3.14 4.53]
[4.53 3.14 2.72 1.73 1.41]
[brown fox jumped quick the]
[the quick jumped fox brown]

 

This is sorted in ascending order. What if you want to sort it in descending order? There is no ready-made function to sort in descending order, but you can easily use a simple for loop to reverse the sorted slice:

    for i := len(integers)/2 - 1; i >= 0; i-- {
        opp := len(integers) - 1 - i
        integers[i], integers[opp] = integers[opp], integers[i]
    }

    fmt.Println(integers)

Simply find the middle of the slice, and then using a loop, exchange the elements with their opposite side, starting from that middle. If you run the preceding snippet, this is what you will get:

[159  53  26  14  3]

You can also use the sort.Slice function, passing in your less function:

    sort.Slice(floats, func(i, j int) bool {
        return floats[i] > floats[j]
    })
    fmt.Println(floats)

This will produce the following output:

[4.53  3.14  2.72  1.73  1.41]

The less function, the second parameter in the sort.Slice function, takes in two parameters i and j , indices of the consecutive elements of the slice. It’s supposed to return true if the element at i is less than the element at j when sorting.

What if the elements are the same? Using sort.Slice means the original order of the elements might be reversed (or remain the same). If you want the order to be consistently the same as the original, you can use sort.SliceStable .

 

The sort.Slice function works with slices of any type, so this means you can also sort custom structs:

复制代码
type Person struct {
    Name string
    Age  uint8
}

func main() {
    people := []Person{
        {"Alice", 22},
        {"Bob", 18},
        {"Charlie", 23},
        {"Dave", 27},
        {"Eve", 31},
    }
    sort.Slice(people, func(i, j int) bool {
        return people[i].Age < people[j].Age
    })
    fmt.Println(people)
}
复制代码

If you run the code you will get the following output, with the people slice sorted according to the ages of the people:

zzh@ZZHPC:/zdata/MyPrograms/Go/study$ go run main.go
[{Bob 18} {Alice 22} {Charlie 23} {Dave 27} {Eve 31}]

Another way of sorting structs is by implementing the sort.Interface .

Here’s how you can do this for the Person struct:

复制代码
type Person struct {
    Name string
    Age  uint8
}

type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

func main() {
    people := []Person{
        {"Alice", 22},
        {"Bob", 18},
        {"Charlie", 23},
        {"Dave", 27},
        {"Eve", 31},
    }
    sort.Sort(ByAge(people))
    fmt.Println(people)
}
复制代码

You want to sort a slice of structs, so you need to associate the interface functions to the slice, not the struct. Create a type named ByAge that is a slice of Person structs. Next, you associate the Len , Less , and Swap functions to ByAge , making it a struct that implements sort.Interface . The Less method here is the same as the one used in the sort.Slice function earlier.

Using this is quite simple. You cast people to ByAge , and pass that into sort.Sort

If you run this code, you will see the following results:

zzh@ZZHPC:/zdata/MyPrograms/Go/study$ go run main.go
[{Bob 18} {Alice 22} {Charlie 23} {Dave 27} {Eve 31}]

Implementing sort.Interface is a bit long-winded, but there are certainly some advantages. For one, you can use sort.Reverse to sort by descending order:

    sort.Sort(sort.Reverse(ByAge(people)))
    fmt.Println(people)

This produces the following output:

zzh@ZZHPC:/zdata/MyPrograms/Go/study$ go run main.go
[{Eve 31} {Dave 27} {Charlie 23} {Alice 22} {Bob 18}]

You can also use the sort.IsSorted function to check if the slice is already sorted:

复制代码
type Person struct {
    Name string
    Age  uint8
}

type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

func main() {
    people := []Person{
        {"Alice", 22},
        {"Bob", 18},
        {"Charlie", 23},
        {"Dave", 27},
        {"Eve", 31},
    }

    b := ByAge(people)
    fmt.Println("b:", b, sort.IsSorted(b)) // false
    fmt.Println("-------------------------------------------------------------------")
    r := sort.Reverse(b)
    fmt.Println("b:", b, sort.IsSorted(b)) // false
    fmt.Println("r:", r, sort.IsSorted(r)) // false
    fmt.Println("-------------------------------------------------------------------")
    sort.Sort(r)
    fmt.Println("b:", b, sort.IsSorted(b)) // false
    fmt.Println("r:", r, sort.IsSorted(r)) // true
    fmt.Println("-------------------------------------------------------------------")
    sort.Sort(b)
    fmt.Println("b:", b, sort.IsSorted(b)) // true
}
复制代码

 

复制代码
zzh@ZZHPC:/zdata/Github/ztest$ go run main.go
b: [{Alice 22} {Bob 18} {Charlie 23} {Dave 27} {Eve 31}] false
-------------------------------------------------------------------
b: [{Alice 22} {Bob 18} {Charlie 23} {Dave 27} {Eve 31}] false
r: &{[{Alice 22} {Bob 18} {Charlie 23} {Dave 27} {Eve 31}]} false
-------------------------------------------------------------------
b: [{Eve 31} {Dave 27} {Charlie 23} {Alice 22} {Bob 18}] false
r: &{[{Eve 31} {Dave 27} {Charlie 23} {Alice 22} {Bob 18}]} true
-------------------------------------------------------------------
b: [{Bob 18} {Alice 22} {Charlie 23} {Dave 27} {Eve 31}] true
复制代码

 

The biggest advantage, though, is that using sort.Interface is a lot more performant than using sort.Slice.

As you can see, using sort.Interface is more efficient. This is because sort.Slice uses any as the first parameter. This means it takes in any structs but is less efficient.

posted on   ZhangZhihuiAAA  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
 
点击右上角即可分享
微信分享提示