• go 结构体多字段多因素排序


    前言

    有时候我们需要处理一份数据,需要多个字段作为条件,联合进行排序。

    代码

    package main
    
    import (
    	"fmt"
    	"sort"
    )
    
    // 数据类型
    type Change struct {
    	user     string
    	language string
    	lines    int
    }
    
    // sort接口方法之一(Less)
    type lessFunc func(p1, p2 *Change) bool
    
    // 数据集类型, 与排序(多字段单独排序)比较, less字段的数据类型不再是 func(p1, p2 *Change) bool
    // 而是 []func(p1, p2 *Change) bool 因为在第一个比较的值相等的情况下, 还要比较第二个值, 所以这里需要多个比较函数
    type multiSorter struct {
    	changes []Change
    	less    []lessFunc
    }
    
    type OrderedBy []lessFunc
    
    func (ob OrderedBy) Sort(changes []Change) {
    	ms := &multiSorter{
    		changes: changes,
    		less:    ob,
    	}
    	sort.Sort(ms)
    }
    
    // Len 为sort接口方法之一
    func (ms *multiSorter) Len() int {
    	return len(ms.changes)
    }
    
    // Swap 为sort接口方法之一
    func (ms *multiSorter) Swap(i, j int) {
    	ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i]
    }
    
    // Less 为sort接口方法之一
    func (ms *multiSorter) Less(i, j int) bool {
    	// 为了后面编写简便, 这里将需要比较的两个元素赋值给两个单独的变量
    	p, q := &ms.changes[i], &ms.changes[j]
    	// Try all but the last comparison.
    	var k int
    	// 由于可能有多个需要排序的字段, 也就对应了多个less函数, 当第一个字段的值相等时,
    	// 需要依次尝试比对后续其他字段的值得大小, 所以这里需要获取比较函数的长度, 以便遍历比较
    	for k = 0; k < len(ms.less)-1; k++ {
    		// 提取比较函数, 将函数赋值到新的变量中以便调用
    		less := ms.less[k]
    		switch {
    		case less(p, q):
    			// 如果 p < q, 返回值为true, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
    			// 如果 p > q, 返回值为false, 则调到下一个case中处理
    			return true
    		case less(q, p):
    			// 如果 p > q, 返回值为false, 不存在两个值相等需要比较后续字段的情况, 所以这里直接返回
    			return false
    		}
    		// 如果代码走到这里, 说明ms.less[k]函数比较后 p == q; 重新开始下一次循环, 更换到下一个比较函数处理
    		continue
    	}
    	// 如果代码走到这里, 说明所有的比较函数执行过后, 所有比较的值都相等
    	// 直接返回最后一次的比较结果数据即可
    	return ms.less[k](p, q)
    }
    
    var changes = []Change{
    	{"gri", "Go", 100},
    	{"ken", "C", 150},
    	{"glenda", "Go", 200},
    	{"rsc", "Go", 200},
    	{"r", "Go", 100},
    	{"ken", "Go", 200},
    	{"dmr", "C", 100},
    	{"r", "C", 150},
    	{"gri", "Smalltalk", 80},
    }
    
    func main() {
    	// 预定义排序函数: 按照姓名升序排列
    	user := func(c1, c2 *Change) bool {
    		return c1.user < c2.user
    	}
    	// 预定义排序函数: 按照语言升序排列
    	language := func(c1, c2 *Change) bool {
    		return c1.language < c2.language
    	}
    	// 预定义排序函数: 按照行数升序排列
    	increasingLines := func(c1, c2 *Change) bool {
    		return c1.lines < c2.lines
    	}
    	//预定义排序函数: 按照行数降序排列
    	decreasingLines := func(c1, c2 *Change) bool {
    		return c1.lines > c2.lines
    	}
    
    	// 按照姓名升序排列
    	OrderedBy([]lessFunc{user}).Sort(changes)
    	fmt.Println("By user:\t\t", changes)
    
    	// 按照姓名升序排列, 姓名相同的按行数升序排列
    	OrderedBy([]lessFunc{user, increasingLines}).Sort(changes)
    	fmt.Println("By user,<lines:\t\t", changes)
    
    	// 按姓名升序排列, 姓名相同的按行数降序排列
    	OrderedBy([]lessFunc{user, decreasingLines}).Sort(changes)
    	fmt.Println("By user,>lines:\t\t", changes)
    
    	// 按语言升序排列, 语言相同按行数升序排列
    	OrderedBy([]lessFunc{language, increasingLines}).Sort(changes)
    	fmt.Println("By language,<lines:\t", changes)
    
    	// 按语言升序排列, 语言相同按行数升序排列, 行数也相同的, 按姓名升序排列
    	OrderedBy([]lessFunc{language, increasingLines, user}).Sort(changes)
    	fmt.Println("By language,<lines,user:", changes)
    }
    

    运行结果

    By user:                 [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
    By user,<lines:          [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
    By user,>lines:          [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
    By language,<lines:      [{dmr C 100} {ken C 150} {r C 150} {r Go 100} {gri Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
    By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
    
  • 相关阅读:
    3.1C#中的命名空间
    2章总结
    2.4冒泡排序
    2.3 C#中的数组
    2.2二重循环
    2.1c#中的循环语句
    1章总结
    docker内外数据拷贝
    搭建docker环境
    centos7 部署Apache的httpd服务器
  • 原文地址:https://www.cnblogs.com/niuben/p/16151309.html
Copyright © 2020-2023  润新知