| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package sort_test |
| |
| import ( |
| "fmt" |
| "sort" |
| ) |
| |
| // A Change is a record of source code changes, recording user, language, and delta size. |
| type Change struct { |
| user string |
| language string |
| lines int |
| } |
| |
| type lessFunc func(p1, p2 *Change) bool |
| |
| // multiSorter implements the Sort interface, sorting the changes within. |
| type multiSorter struct { |
| changes []Change |
| less []lessFunc |
| } |
| |
| // Sort sorts the argument slice according to the less functions passed to OrderedBy. |
| func (ms *multiSorter) Sort(changes []Change) { |
| ms.changes = changes |
| sort.Sort(ms) |
| } |
| |
| // OrderedBy returns a Sorter that sorts using the less functions, in order. |
| // Call its Sort method to sort the data. |
| func OrderedBy(less ...lessFunc) *multiSorter { |
| return &multiSorter{ |
| less: less, |
| } |
| } |
| |
| // Len is part of sort.Interface. |
| func (ms *multiSorter) Len() int { |
| return len(ms.changes) |
| } |
| |
| // Swap is part of sort.Interface. |
| func (ms *multiSorter) Swap(i, j int) { |
| ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i] |
| } |
| |
| // Less is part of sort.Interface. It is implemented by looping along the |
| // less functions until it finds a comparison that discriminates between |
| // the two items (one is less than the other). Note that it can call the |
| // less functions twice per call. We could change the functions to return |
| // -1, 0, 1 and reduce the number of calls for greater efficiency: an |
| // exercise for the reader. |
| 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 |
| for k = 0; k < len(ms.less)-1; k++ { |
| less := ms.less[k] |
| switch { |
| case less(p, q): |
| // p < q, so we have a decision. |
| return true |
| case less(q, p): |
| // p > q, so we have a decision. |
| return false |
| } |
| // p == q; try the next comparison. |
| } |
| // All comparisons to here said "equal", so just return whatever |
| // the final comparison reports. |
| 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}, |
| } |
| |
| // ExampleMultiKeys demonstrates a technique for sorting a struct type using different |
| // sets of multiple fields in the comparison. We chain together "Less" functions, each of |
| // which compares a single field. |
| func Example_sortMultiKeys() { |
| // Closures that order the Change structure. |
| 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 // Note: > orders downwards. |
| } |
| |
| // Simple use: Sort by user. |
| OrderedBy(user).Sort(changes) |
| fmt.Println("By user:", changes) |
| |
| // More examples. |
| OrderedBy(user, increasingLines).Sort(changes) |
| fmt.Println("By user,<lines:", changes) |
| |
| OrderedBy(user, decreasingLines).Sort(changes) |
| fmt.Println("By user,>lines:", changes) |
| |
| OrderedBy(language, increasingLines).Sort(changes) |
| fmt.Println("By language,<lines:", changes) |
| |
| OrderedBy(language, increasingLines, user).Sort(changes) |
| fmt.Println("By language,<lines,user:", changes) |
| |
| // Output: |
| // 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}] |
| |
| } |