原文:CalliCoder — Golang Sorting and Custom Sorting by functions
排序是我們平常撰寫程式時常見的用法。在本文中,你將學到如何在 Go 中對原始類型(string、int、float64)和自訂的集合進行排序。
在 Go 中對字串、整數或浮點數的 slice 進行排序
Go 的 sort package 提供了幾種方便的方法來對原始類型的 slice 進行排序。以下範例示範了如何在 Go 中對字串、整數和浮點數的 slice 進行排序:
package main
import (
"fmt"
"sort"
)
func main() {
// Sorting a slice of Strings
strs := []string{"quick", "brown", "fox", "jumps"}
sort.Strings(strs)
fmt.Println("Sorted strings: ", strs)
// Sorting a slice of Integers
ints := []int{56, 19, 78, 67, 14, 25}
sort.Ints(ints)
fmt.Println("Sorted integers: ", ints)
// Sorting a slice of Floats
floats := []float64{176.8, 19.5, 20.8, 57.4}
sort.Float64s(floats)
fmt.Println("Sorted floats: ", floats)
}
$ go run sorting.go
Sorted strings: [brown fox jumps quick]
Sorted integers: [14 19 25 56 67 78]
Sorted floats: [19.5 20.8 57.4 176.8]
以相反順序對 slice 進行排序
要以相反順序對 slice 進行排序,你可以在 StringSlice
、IntSlice
和 Float64Slice
類型上使用 Reverse()
函數。這些類型實作了 sort
package 中定義用來比較和交換的 Interface
。
Reverse()
函數返回了相反順序的資料。讓我們來看一個以相反順序對字串、整數和浮點數的 slice 進行排序的範例:
package main
import (
"fmt"
"sort"
)
func main() {
// Sorting a slice of Strings
strs := []string{"quick", "brown", "fox", "jumps"}
sort.Sort(sort.Reverse(sort.StringSlice(strs)))
fmt.Println("Sorted strings in reverse order: ", strs)
// Sorting a slice of Integers
ints := []int{56, 19, 78, 67, 14, 25}
sort.Sort(sort.Reverse(sort.IntSlice(ints)))
fmt.Println("Sorted integers in reverse order: ", ints)
// Sorting a slice of Floats
floats := []float64{176.8, 19.5, 20.8, 57.4}
sort.Sort(sort.Reverse(sort.Float64Slice(floats)))
fmt.Println("Sorted floats in reverse order: ", floats)
}
$ go run reverse-sorting.go
Sorted strings in reverse order: [quick jumps fox brown]
Sorted integers in reverse order: [78 67 56 25 19 14]
Sorted floats in reverse order: [176.8 57.4 20.8 19.5]
在 Go 中使用比較器函數對 slice 進行排序
很多時候,你可能希望按照自然順序以外的方式對集合進行排序。例如,你想按照長度對字串 slice 進行排序,或者按照年齡對使用者進行排序。
對於這些案例,你可以使用 sort
package 提供的 Slice()
和 SliceStable()
函數。這些是能接受 less
函數作為參數的高階函數:
func Slice(x interface{}, less func(i, j int) bool)
func SliceStable(x interface{}, less func(i, j int) bool)
less
函數是一個比較器函數,它回報索引 i
處的元素是否應該排在索引 j
處的元素前。如果 less(i, j)
和 less(j, i)
都是返回 false
,那麼索引 i
和 j
處的元素被視為是相同的。
當使用 Slice()
函數時,排序不保證穩定:相等的元素可能會和它們原本的順序顛倒。要穩定的排序,請使用 SliceStable()
。
讓我們來看個範例:
package main
import (
"fmt"
"sort"
)
func main() {
// Sorting a slice of strings by length
strs := []string{"United States", "India", "France", "United Kingdom", "Spain"}
sort.Slice(strs, func(i, j int) bool {
return len(strs[i]) < len(strs[j])
})
fmt.Println("Sorted strings by length: ", strs)
// Stable sort
sort.SliceStable(strs, func(i, j int) bool {
return len(strs[i]) < len(strs[j])
})
fmt.Println("[Stable] Sorted strings by length: ", strs)
// Sorting a slice of strings in the reverse order of length
sort.SliceStable(strs, func(i, j int) bool {
return len(strs[j]) < len(strs[i])
})
fmt.Println("[Stable] Sorted strings by reverse order of length: ", strs)
}
$ go run sorting-by-comparator.go
Sorted strings by length: [India Spain France United States United Kingdom]
[Stable] Sorted strings by length: [India Spain France United States United Kingdom]
[Stable] Sorted strings by reverse order of length: [United Kingdom United States France India Spain]
在 Go 中使用比較器函數對 struce slice 進行排序
延續上一個範例,此範例將示範如何透過將自訂的 less
函數傳遞給 Slice()
和 SliceStable()
函數,來對使用者自訂的 struct 進行排序:
package main
import (
"fmt"
"sort"
)
type User struct {
Name string
Age int
}
func main() {
// Sorting a slice of structs by a field
users := []User{
{
Name: "Rajeev",
Age: 28,
},
{
Name: "Monica",
Age: 31,
},
{
Name: "John",
Age: 56,
},
{
Name: "Amanda",
Age: 16,
},
{
Name: "Steven",
Age: 28,
},
}
// Sort users by their age
sort.Slice(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})
fmt.Println("Sorted users by age: ", users)
// Stable sort
sort.SliceStable(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})
fmt.Println("Sorted users by age: ", users)
}
透過實作 sort.Interface 來自訂排序
最後,讓我們看一下在 Go 中對原始類型的集合或使用者自訂的 struct 進行排序的最通用方式。要啟用對任何類型集合的自訂排序,你需要定義一個相對應的類型並實作 sort
package 提供的通用 Interface
。該 Interface
包含以下方法:
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with index i must sort before the element with index j.
// If both Less(i, j) and Less(j, i) are false, then the elements at index i and j are considered equal.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
實作上述 Interface
後,你可以使用 Sort()
或 Stable()
函數對任何實作 sort.Interface
介面的集合進行排序。
讓我們透過一個範例來理解:
package main
import (
"fmt"
"sort"
)
type User struct {
Name string
Age int
}
// Define a collection type that implements sort.Interface
type UsersByAge []User
func (u UsersByAge) Len() int {
return len(u)
}
func (u UsersByAge) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
func (u UsersByAge) Less(i, j int) bool {
return u[i].Age < u[j].Age
}
func main() {
users := []User{
{
Name: "Rajeev",
Age: 28,
},
{
Name: "Monica",
Age: 31,
},
{
Name: "John",
Age: 56,
},
{
Name: "Amanda",
Age: 16,
},
{
Name: "Steven",
Age: 28,
},
}
// Sorting a slice of users by age (Sort may place equal elements in any order in the final result)
sort.Sort(UsersByAge(users))
fmt.Println("Sorted users by age: ", users)
// Stable Sorting (Stavle sort preserves the original input order of equal elements)
sort.Stable(UsersByAge(users))
fmt.Println("[Stable] Sorted users by age: ", users)
}
$ go run custom-sorting-struct.go
Sorted users by age: [{Amanda 16} {Rajeev 28} {Steven 28} {Monica 31} {John 56}]
[Stable] Sorted users by age: [{Amanda 16} {Rajeev 28} {Steven 28} {Monica 31} {John 56}]