diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 0fa2563c52..cc382a1b2f 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -103,6 +103,10 @@ "ImportPath": "github.com/mesos/mesos-go/upid", "Rev": "83b52d7f648d2a2fa91330123d348d4090be2aed" }, + { + "ImportPath": "github.com/skarademir/naturalsort", + "Rev": "69a5d87bef620f77ee8508db30c846b3b84b111e" + }, { "ImportPath": "github.com/samalba/dockerclient", "Rev": "68832c185bb6304fed26a986891fd27b6ed17987" diff --git a/Godeps/_workspace/src/github.com/skarademir/naturalsort/LICENSE.md b/Godeps/_workspace/src/github.com/skarademir/naturalsort/LICENSE.md new file mode 100644 index 0000000000..af513860b4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/skarademir/naturalsort/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 skarademir + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/skarademir/naturalsort/README.md b/Godeps/_workspace/src/github.com/skarademir/naturalsort/README.md new file mode 100644 index 0000000000..60307fc049 --- /dev/null +++ b/Godeps/_workspace/src/github.com/skarademir/naturalsort/README.md @@ -0,0 +1,22 @@ +# naturalsort +A simple natural string sorter for Go. + +##Usage +Implements the `sort.Interface` + +called by `sort.Sort(NaturalSort([]string))` +###Example + +```go +SampleStringArray := []string{ + "z24", "z2", "z15", "z1", + "z3", "z20", "z5", "z11", + "z 21", "z22"} +sort.Sort(NaturalSort(SampleStringArray)) +``` + +##Needless Description +Inspired by [Jeff Atwood's seminal blog post](http://blog.codinghorror.com/sorting-for-humans-natural-sort-order/) and +structured similarly to [Ian Griffiths' C# implementation](http://www.interact-sw.co.uk/iangblog/2007/12/13/natural-sorting). +This uses a regex to split the numeric and non-numeric portions of the string into a chunky array. Next, the left and right sides' +chunks are compared either by string comparrison (if either chunk is a non-numeric), or by ~~integer (if both chunks are numeric)~~ a character-by-character iterative function that compares numerical strings diff --git a/Godeps/_workspace/src/github.com/skarademir/naturalsort/naturalsort.go b/Godeps/_workspace/src/github.com/skarademir/naturalsort/naturalsort.go new file mode 100644 index 0000000000..23dd16d163 --- /dev/null +++ b/Godeps/_workspace/src/github.com/skarademir/naturalsort/naturalsort.go @@ -0,0 +1,60 @@ +package naturalsort + +import ( + "regexp" + "strings" +) + +type NaturalSort []string + +var r = regexp.MustCompile(`[^0-9]+|[0-9]+`) + +func (s NaturalSort) Len() int { + return len(s) +} +func (s NaturalSort) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s NaturalSort) Less(i, j int) bool { + + spliti := r.FindAllString(strings.Replace(s[i], " ", "", -1), -1) + splitj := r.FindAllString(strings.Replace(s[j], " ", "", -1), -1) + + for index := 0; index < len(spliti) && index < len(splitj); index++ { + if spliti[index] != splitj[index] { + // Both slices are numbers + if isNumber(spliti[index][0]) && isNumber(splitj[index][0]) { + // Remove Leading Zeroes + stringi := strings.TrimLeft(spliti[index], "0") + stringj := strings.TrimLeft(splitj[index], "0") + if len(stringi) == len(stringj) { + for indexchar := 0; indexchar < len(stringi); indexchar++ { + if stringi[indexchar] != stringj[indexchar] { + return stringi[indexchar] < stringj[indexchar] + } + } + return len(spliti[index]) < len(splitj[index]) + } + return len(stringi) < len(stringj) + } + // One of the slices is a number (we give precedence to numbers regardless of ASCII table position) + if isNumber(spliti[index][0]) || isNumber(splitj[index][0]) { + return isNumber(spliti[index][0]) + } + // Both slices are not numbers + return spliti[index] < splitj[index] + } + + } + // Fall back for cases where space characters have been annihliated by the replacment call + // Here we iterate over the unmolsested string and prioritize numbers over + for index := 0; index < len(s[i]) && index < len(s[j]); index++ { + if isNumber(s[i][index]) || isNumber(s[j][index]) { + return isNumber(s[i][index]) + } + } + return s[i] < s[j] +} +func isNumber(input uint8) bool { + return input >= '0' && input <= '9' +} diff --git a/Godeps/_workspace/src/github.com/skarademir/naturalsort/naturalsort_test.go b/Godeps/_workspace/src/github.com/skarademir/naturalsort/naturalsort_test.go new file mode 100644 index 0000000000..83873c4c2a --- /dev/null +++ b/Godeps/_workspace/src/github.com/skarademir/naturalsort/naturalsort_test.go @@ -0,0 +1,102 @@ +package naturalsort + +import ( + "reflect" + "sort" + "testing" +) + +func TestSortValid(t *testing.T) { + cases := []struct { + data, expected []string + }{ + { + nil, + nil, + }, + { + []string{}, + []string{}, + }, + { + []string{"a"}, + []string{"a"}, + }, + { + []string{"0"}, + []string{"0"}, + }, + { + []string{"data", "data20", "data3"}, + []string{"data", "data3", "data20"}, + }, + { + []string{"1", "2", "30", "22", "0", "00", "3"}, + []string{"0", "00", "1", "2", "3", "22", "30"}, + }, + { + []string{"A1", "A0", "A21", "A11", "A111", "A2"}, + []string{"A0", "A1", "A2", "A11", "A21", "A111"}, + }, + { + []string{"A1BA1", "A11AA1", "A2AB0", "B1AA1", "A1AA1"}, + []string{"A1AA1", "A1BA1", "A2AB0", "A11AA1", "B1AA1"}, + }, + { + []string{"1ax10", "1a10", "1ax2", "1ax"}, + []string{"1a10", "1ax", "1ax2", "1ax10"}, + }, + { + []string{"z1a10", "z1ax2", "z1ax"}, + []string{"z1a10", "z1ax", "z1ax2"}, + }, + { + // regression test for #8 + []string{"a0000001", "a0001"}, + []string{"a0001", "a0000001"}, + }, + { + // regression test for #10 - Number sort before any symbols even if theyre lower on the ASCII table + []string{"#1", "1", "_1", "a"}, + []string{"1", "#1", "_1", "a"}, + }, + { + // regression test for #10 - Number sort before any symbols even if theyre lower on the ASCII table + []string{"#1", "1", "_1", "a"}, + []string{"1", "#1", "_1", "a"}, + }, + { // test correct handling of space-only strings + []string{"1", " ", "0"}, + []string{"0", "1", " "}, + }, + { // test correct handling of multiple spaces being correctly ordered AFTER numbers + []string{"1", " ", " 1", " "}, + []string{"1", " ", " 1", " "}, + }, + { + []string{"1", "#1", "a#", "a1"}, + []string{"1", "#1", "a1", "a#"}, + }, + { + // regression test for #10 + []string{"111111111111111111112", "111111111111111111113", "1111111111111111111120"}, + []string{"111111111111111111112", "111111111111111111113", "1111111111111111111120"}, + }, + } + + for i, c := range cases { + sort.Sort(NaturalSort(c.data)) + if !reflect.DeepEqual(c.data, c.expected) { + t.Fatalf("Wrong order in test case #%d.\nExpected=%v\nGot=%v", i, c.expected, c.data) + } + } + +} + +func BenchmarkSort(b *testing.B) { + var data = [...]string{"A1BA1", "A11AA1", "A2AB0", "B1AA1", "A1AA1"} + for ii := 0; ii < b.N; ii++ { + d := NaturalSort(data[:]) + sort.Sort(d) + } +}