github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/sort/example_multi_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sort_test 6 7 import ( 8 "fmt" 9 "sort" 10 ) 11 12 // A Change is a record of source code changes, recording user, language, and delta size. 13 type Change struct { 14 user string 15 language string 16 lines int 17 } 18 19 type lessFunc func(p1, p2 *Change) bool 20 21 // multiSorter implements the Sort interface, sorting the changes within. 22 type multiSorter struct { 23 changes []Change 24 less []lessFunc 25 } 26 27 // Sort sorts the argument slice according to the less functions passed to OrderedBy. 28 func (ms *multiSorter) Sort(changes []Change) { 29 ms.changes = changes 30 sort.Sort(ms) 31 } 32 33 // OrderedBy returns a Sorter that sorts using the less functions, in order. 34 // Call its Sort method to sort the data. 35 func OrderedBy(less ...lessFunc) *multiSorter { 36 return &multiSorter{ 37 less: less, 38 } 39 } 40 41 // Len is part of sort.Interface. 42 func (ms *multiSorter) Len() int { 43 return len(ms.changes) 44 } 45 46 // Swap is part of sort.Interface. 47 func (ms *multiSorter) Swap(i, j int) { 48 ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i] 49 } 50 51 // Less is part of sort.Interface. It is implemented by looping along the 52 // less functions until it finds a comparison that is either Less or 53 // !Less. Note that it can call the less functions twice per call. We 54 // could change the functions to return -1, 0, 1 and reduce the 55 // number of calls for greater efficiency: an exercise for the reader. 56 func (ms *multiSorter) Less(i, j int) bool { 57 p, q := &ms.changes[i], &ms.changes[j] 58 // Try all but the last comparison. 59 var k int 60 for k = 0; k < len(ms.less)-1; k++ { 61 less := ms.less[k] 62 switch { 63 case less(p, q): 64 // p < q, so we have a decision. 65 return true 66 case less(q, p): 67 // p > q, so we have a decision. 68 return false 69 } 70 // p == q; try the next comparison. 71 } 72 // All comparisons to here said "equal", so just return whatever 73 // the final comparison reports. 74 return ms.less[k](p, q) 75 } 76 77 var changes = []Change{ 78 {"gri", "Go", 100}, 79 {"ken", "C", 150}, 80 {"glenda", "Go", 200}, 81 {"rsc", "Go", 200}, 82 {"r", "Go", 100}, 83 {"ken", "Go", 200}, 84 {"dmr", "C", 100}, 85 {"r", "C", 150}, 86 {"gri", "Smalltalk", 80}, 87 } 88 89 // ExampleMultiKeys demonstrates a technique for sorting a struct type using different 90 // sets of multiple fields in the comparison. We chain together "Less" functions, each of 91 // which compares a single field. 92 func Example_sortMultiKeys() { 93 // Closures that order the Change structure. 94 user := func(c1, c2 *Change) bool { 95 return c1.user < c2.user 96 } 97 language := func(c1, c2 *Change) bool { 98 return c1.language < c2.language 99 } 100 increasingLines := func(c1, c2 *Change) bool { 101 return c1.lines < c2.lines 102 } 103 decreasingLines := func(c1, c2 *Change) bool { 104 return c1.lines > c2.lines // Note: > orders downwards. 105 } 106 107 // Simple use: Sort by user. 108 OrderedBy(user).Sort(changes) 109 fmt.Println("By user:", changes) 110 111 // More examples. 112 OrderedBy(user, increasingLines).Sort(changes) 113 fmt.Println("By user,<lines:", changes) 114 115 OrderedBy(user, decreasingLines).Sort(changes) 116 fmt.Println("By user,>lines:", changes) 117 118 OrderedBy(language, increasingLines).Sort(changes) 119 fmt.Println("By language,<lines:", changes) 120 121 OrderedBy(language, increasingLines, user).Sort(changes) 122 fmt.Println("By language,<lines,user:", changes) 123 124 // Output: 125 // By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}] 126 // 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}] 127 // 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}] 128 // By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}] 129 // 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}] 130 131 }