背景

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)
}

演示