mirror of
https://github.com/golang/go
synced 2024-09-25 01:20:13 -06:00
sort: new example: programmatic sort by multiple keys
Demonstrates one way to sort a slice of structs according to different sort criteria, done in sequence. One possible answer to a question that comes up often. R=golang-dev, gri, bradfitz, adg, adg, rogpeppe CC=golang-dev https://golang.org/cl/8182044
This commit is contained in:
parent
4432be3b28
commit
15f276bc53
132
src/pkg/sort/example_multi_test.go
Normal file
132
src/pkg/sort/example_multi_test.go
Normal file
@ -0,0 +1,132 @@
|
||||
// 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
|
||||
// ordering functions until it finds a comparison that is either Less or
|
||||
// !Less. Note that it can call the ordering functions twice per call. We
|
||||
// could change the ordering 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}]
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user