有了go语言中的排序接口, 可以很容易的对结构体的任意一个字段进行排序, 具体以哪个字段排序, 定义在该结构体Slice的Less
方法中. 一个结构体的Slice仅可以实现一次sort
接口, 如果一个结构体中有多个字段需要单独进行排序, 最粗鲁的办法只能是定义多个Slice类型, 为每个类型都编写不同的Less
方法
约定关键字:
多字段单独排序: 一个结构体中, 需要有多个字段作为依据, 单独进行排序, 单个字段排序时不受其他字段排序策略的影响
多字段多因素排序: 一个结构体中, 对第一个字段排序时, 如果该字段的数值相等, 按其他字段再次进行排序操作(例如: 按年龄进行排序, 年龄相同的按体重排序)
结构体单字段排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package main
import ( "fmt" "sort" )
type student struct { name string age uint weight float32 }
type students []student
func (s students) Len() int { return len(s) }
func (s students) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s students) Less(i, j int) bool { return s[i].age < s[j].age }
var ss = students{ {"d", 7, 28}, {"c", 3, 40}, {"b", 8, 35}, {"a", 5, 30}, }
func main() { sort.Sort(ss) fmt.Println(ss) }
|
运行结果:
1
| [{c 3 40} {a 5 30} {d 7 28} {b 8 35}]
|
结构体多字段单独排序
多字段需要排序的, 最粗鲁的办法是为每一个需要排序的字段单独声明一个数据集类型, 然后在每个数据集类型中实现其对应的Less
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| package main
import ( "fmt" "sort" )
type student struct { name string age uint weight float32 }
type studentsSortByName []student
func (s studentsSortByName) Len() int { return len(s) }
func (s studentsSortByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s studentsSortByName) Less(i, j int) bool { return s[i].name < s[j].name }
type studentsSortByAge []student
func (s studentsSortByAge) Len() int { return len(s) }
func (s studentsSortByAge) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s studentsSortByAge) Less(i, j int) bool { return s[i].age < s[j].age }
type studentsSortByWeight []student
func (s studentsSortByWeight) Len() int { return len(s) }
func (s studentsSortByWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s studentsSortByWeight) Less(i, j int) bool { return s[i].weight < s[j].weight }
var ss = []student{ {"d", 7, 28}, {"c", 3, 40}, {"b", 8, 35}, {"a", 5, 30}, }
func main() { sort.Sort(studentsSortByName(ss)) fmt.Println(ss)
sort.Sort(studentsSortByAge(ss)) fmt.Println(ss)
sort.Sort(studentsSortByWeight(ss)) fmt.Println(ss) }
|
运行结果:
1 2 3
| [{a 5 30} {b 8 35} {c 3 40} {d 7 28}] // 按姓名排序 [{c 3 40} {a 5 30} {d 7 28} {b 8 35}] // 按年龄排序 [{d 7 28} {a 5 30} {b 8 35} {c 3 40}] // 按体重排序
|
这个排序的办法的劣势是需要写很多重复代码, 通过这个例子也可以看出, 按不动字段排序实际只是Less
方法的实现不同
结构体多字段单独排序(推荐)
这里介绍一个更优雅的方式, 解决上面多字段排序需要编写n * 3
方法的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| package main
import ( "fmt" "sort" )
type student struct { name string age uint weight float32 }
type studentSorter struct { students []student by func(i, j student) bool // 排序函数, 排序的依据, 具体排序的策略, 会被放置在这里 }
func (s studentSorter) Len() int { return len(s.students) }
func (s studentSorter) Swap(i, j int) { s.students[i], s.students[j] = s.students[j], s.students[i] }
func (s studentSorter) Less(i, j int) bool { return s.by(s.students[i], s.students[j]) }
var students = []student{ {"d", 7, 28}, {"c", 3, 40}, {"b", 8, 35}, {"a", 5, 30}, }
type By func(i, j student) bool func (b By) Sort (students []student) { ss := studentSorter{ students: students, by: b, } sort.Sort(ss) }
var name = func(i, j student) bool { return i.name < j.name }
var age = func(i, j student) bool { return i.age < j.age }
var weight = func(i, j student) bool { return i.weight < j.weight }
func main() { By(name).Sort(students) fmt.Println("By name:", students)
By(age).Sort(students) fmt.Println("By age:", students)
By(weight).Sort(students) fmt.Println("By weight:", students) }
|
运行结果:
1 2 3
| By name: [{a 5 30} {b 8 35} {c 3 40} {d 7 28}] By age: [{c 3 40} {a 5 30} {d 7 28} {b 8 35}] By weight: [{d 7 28} {a 5 30} {b 8 35} {c 3 40}]
|
这个多字段单独排序的方法, 省去了需要重复编写Len()``Swap()
方法的麻烦, 也是sort
包官方推荐的多字段排序案例
结构体多字段单独排序2(推荐)
官方文档中还介绍了另一种排序方式, 通过Go语言独特的组合特性来实现sort接口的三个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package main
import ( "fmt" "sort" )
type student struct { name string age int weight int }
type students []student
func (s students) Len() int { return len(s) } func (s students) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type ByName struct { students }
func (s ByName) Less(i, j int) bool { return s.students[i].name < s.students[j].name }
type ByWeight struct { students }
func (s ByWeight) Less(i, j int) bool { return s.students[i].weight < s.students[j].weight }
var ss = []student{ {"d", 7, 28}, {"c", 3, 40}, {"b", 8, 35}, {"a", 5, 30}, }
func main() { sort.Sort(ByWeight{ss}) fmt.Println("Organs by weight: ", ss)
sort.Sort(ByName{ss}) fmt.Println("Organs by name: ", ss) }
|