背景
go提供了几种内置类型的排序函数,例如 sort.Strings、 sort.Float64、 sort.Ints, 但是我们在业务中需要根据业务属性进行排序例如,某个struct中age, createTime 或者priority等等。 本文参考https://gobyexample.com/sorting-by-functions, 给出一个简单自定义排序示例
实现
本示例中字符串是固定格式的{正整数}-{字符串}, 排序只和正整数相关,与后面的字符串无关,因此需要先提取其中的正整数,然后转为数字,最后再比较。
package main
import (
"fmt"
"sort"
"k8s.io/klog"
"strings"
"strconv"
)
type byPrefix []string
func (s byPrefix) Len() int {
return len(s)
}
func GetPrefix(s string) int {
subStrs := strings.Split(s, "-")
if len(subStrs) != 0 {
numStr := subStrs[0]
i, err := strconv.Atoi(numStr)
if err != nil {
klog.Infof("faild to conver to int from %q, err:%v", numStr, s, err)
} else {
return i
}
}
return -1
}
func (s byPrefix) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s byPrefix) Less(i, j int) bool {
return GetPrefix(s[i]) < GetPrefix(s[j])
}
func main() {
strs := []string{"1-create", "2-changeClass", "3-changePara",
"10-resart","11-changePara","20-switch", "21-restart", "30-switch", "31-changePara",
"100-changeClass", "101-changePara", "110-changePara",
"120-openProxy", "121-changeClass", "130-swith", "131-changePara"}
//按照默认string排序100-changeClass会排在2-changeClass前面,这不是期望的结果
klog.Info("---默认字符串排序---")
sort.Strings(strs)
fmt.Println("Strings:", strs)
klog.Info("---自定义排序---")
sort.Sort(byPrefix(strs))
fmt.Println("Strings:", strs)
ints := []int{1, 2, 3,
10, 11, 20, 21, 30, 31,
100, 101, 110, 120, 121, 130, 131}
sort.Ints(ints)
fmt.Println("Ints: ", ints)
s := sort.IntsAreSorted(ints)
fmt.Println("Sorted: ", s)
}
其中byPrefix实现了https://pkg.go.dev/sort?utm_source=gopls#Sort中的 sort.Interface - Len, Less, and Swap 方法
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.
// Sort may place equal elements in any order in the final result,
// while Stable preserves the original input order of equal elements.
//
// Less must describe a transitive ordering:
// - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well.
// - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well.
//
// Note that floating-point comparison (the < operator on float32 or float64 values)
// is not a transitive ordering when not-a-number (NaN) values are involved.
// See Float64Slice.Less for a correct implementation for floating-point values.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}