133 lines
4.1 KiB
Go
133 lines
4.1 KiB
Go
// 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) {
|
|
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{
|
|
changes: changes,
|
|
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 is either Less or
|
|
// !Less. 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)
|
|
|
|
// multiSorter implements the Sort interface, so we can also do this.
|
|
sort.Sort(OrderedBy(user, increasingLines))
|
|
fmt.Println("By user,<lines:", changes)
|
|
|
|
// More examples.
|
|
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 Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {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} {gri Go 100} {r 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}]
|
|
|
|
}
|