From 5594a438c1d729fe1c60f452d25f1b9a18254207 Mon Sep 17 00:00:00 2001 From: Mark McGranaghan Date: Tue, 9 Oct 2012 12:05:43 -0700 Subject: [PATCH] sorting-by-functions --- .../sorting-by-functions.go | 77 ++++++++----------- .../sorting-by-functions.sh | 14 +++- tool/measure.go | 4 +- 3 files changed, 46 insertions(+), 49 deletions(-) diff --git a/src/sorting-by-functions/sorting-by-functions.go b/src/sorting-by-functions/sorting-by-functions.go index c68e30d..558ea62 100644 --- a/src/sorting-by-functions/sorting-by-functions.go +++ b/src/sorting-by-functions/sorting-by-functions.go @@ -1,54 +1,45 @@ // ## Sorting by Functions -// Sorting a slice by a function is a bit tricker in Go -// than you may be used to in other languages. Let's look -// at some examples to see how it works. +// Sometimes we'll want to sort a collection by something +// other than its natural order. For example, suppose we +// wanted to sort strings by their length instead of +// alphabetically. Here's an example of custom sorts sorts +// in Go. package main -import "fmt" import "sort" +import "fmt" -type Person struct { - Name string - Age int -} - -type ByName []Person - -func (this ByName) Len() int { - return len(this) -} -func (this ByName) Less(i, j int) bool { - return this[i].Name < this[j].Name -} -func (this ByName) Swap(i, j int) { - this[i], this[j] = this[j], this[i] -} - -type ByAge []Person - -func (this ByAge) Len() int { - return len(this) -} -func (this ByAge) Less(i, j int) bool { - return this[i].Age < this[j].Age -} -func (this ByAge) Swap(i, j int) { - this[i], this[j] = this[j], this[i] +// In order to sort by a custom function in Go, we need a +// corresponding type. Here we've created a `ByLength` +// type that is just an alias for the builtin `[]string` +// type. +type ByLength []string + +// We implement `sort.Interface` - `Len`, `Less`, and +// `Swap` - on our type so we can use the `sort` package's +// generic `Sort` function. `Len` and `Swap` +// will usually be similar accross types and `Less` will +// hold the actual custom sorting logic. In our case we +// want to sort in order of increasing string length, so +// we use `len(s[i])` and `len(s[j])` here. +func (s ByLength) Len() int { + return len(s) +} +func (s ByLength) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s ByLength) Less(i, j int) bool { + return len(s[i]) < len(s[j]) } +// With all of this in place, we can now implement our +// custom sort by casting the original `fruits` slice to +// `ByLength`, and then use `sort.Sort` on that typed +// slice. func main() { - kids := []Person{ - {"Jack", 10}, - {"Jill", 9}, - {"Bob", 12}, - } - fmt.Println("Original:", kids) - - sort.Sort(ByName(kids)) - fmt.Println("ByName: ", kids) - - sort.Sort(ByAge(kids)) - fmt.Println("ByAge: ", kids) + fruits := []string{"peach", "banana", "kiwi"} + sort.Sort(ByLength(fruits)) + fmt.Println(fruits) } diff --git a/src/sorting-by-functions/sorting-by-functions.sh b/src/sorting-by-functions/sorting-by-functions.sh index 7610d44..d2a1c4c 100644 --- a/src/sorting-by-functions/sorting-by-functions.sh +++ b/src/sorting-by-functions/sorting-by-functions.sh @@ -1,4 +1,10 @@ -$ go run sorting-by-function.go -Original: [{Jack 10} {Jill 9} {Bob 12}] -ByName: [{Bob 12} {Jack 10} {Jill 9}] -ByAge: [{Jill 9} {Jack 10} {Bob 12}] +# Running our program shows a list sorted by string +# length, as desired. +$ go run sorting-by-functions.go +[kiwi peach banana] + +# By following this same pattern of creating a custom +# type, implementing the three `Interface` methods on that +# type, and then calling sort.Sort on a collection of that +# custom type, we can sort Go slices by arbitrary +# functions. diff --git a/tool/measure.go b/tool/measure.go index 3fd0b23..45d1671 100644 --- a/tool/measure.go +++ b/tool/measure.go @@ -30,9 +30,9 @@ func main() { for _, sourcePath := range sourcePaths { foundLongLine := false lines := readLines(sourcePath) - for _, line := range lines { + for i, line := range lines { if !foundLongLine && !todoPat.MatchString(line) && (len(line) > 58) { - fmt.Println("measure:", sourcePath) + fmt.Printf("measure: %s:%d\n", sourcePath, i+1) foundLongLine = true foundLongFile = true }