Skip to content
Snippets Groups Projects
example_multi_test.go 4.01 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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 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)
    
    
    	// 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}]