go 結構體多欄位多因素排序
阿新 • • 發佈:2022-04-15
前言
有時候我們需要處理一份要根據多個欄位進行排序的資料。
程式碼
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}]