RosettaCodeData/Task/Natural-sorting/Go/natural-sorting.go

135 lines
3.0 KiB
Go

package main
import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
)
var tests = []struct {
descr string
list []string
}{
{"Ignoring leading spaces", []string{
"ignore leading spaces: 2-2",
" ignore leading spaces: 2-1",
" ignore leading spaces: 2+0",
" ignore leading spaces: 2+1",
}},
{"Ignoring multiple adjacent spaces", []string{
"ignore m.a.s spaces: 2-2",
"ignore m.a.s spaces: 2-1",
"ignore m.a.s spaces: 2+0",
"ignore m.a.s spaces: 2+1",
}},
{"Equivalent whitespace characters", []string{
"Equiv. spaces: 3-3",
"Equiv.\rspaces: 3-2",
"Equiv.\fspaces: 3-1",
"Equiv.\bspaces: 3+0",
"Equiv.\nspaces: 3+1",
"Equiv.\tspaces: 3+2",
}},
{"Case Indepenent sort", []string{
"cASE INDEPENENT: 3-2",
"caSE INDEPENENT: 3-1",
"casE INDEPENENT: 3+0",
"case INDEPENENT: 3+1",
}},
{"Numeric fields as numerics", []string{
"foo100bar99baz0.txt",
"foo100bar10baz0.txt",
"foo1000bar99baz10.txt",
"foo1000bar99baz9.txt",
}},
}
func main() {
for _, test := range tests {
fmt.Println(test.descr)
fmt.Println("Input order:")
for _, s := range test.list {
fmt.Printf(" %q\n", s)
}
fmt.Println("Natural order:")
l := make(list, len(test.list))
for i, s := range test.list {
l[i] = newNatStr(s)
}
sort.Sort(l)
for _, s := range l {
fmt.Printf(" %q\n", s.s)
}
fmt.Println()
}
}
// natStr associates a string with a preprocessed form
type natStr struct {
s string // original
t []tok // preprocessed "sub-fields"
}
func newNatStr(s string) (t natStr) {
t.s = s
s = strings.ToLower(strings.Join(strings.Fields(s), " "))
x := dx.FindAllString(s, -1)
t.t = make([]tok, len(x))
for i, s := range x {
if n, err := strconv.Atoi(s); err == nil {
t.t[i].n = n
} else {
t.t[i].s = s
}
}
return t
}
var dx = regexp.MustCompile(`\d+|\D+`)
// rule is to use s unless it is empty, then use n
type tok struct {
s string
n int
}
// rule 2 of "numeric sub-fields" from talk page
func (f1 tok) Cmp(f2 tok) int {
switch {
case f1.s == "":
switch {
case f2.s > "" || f1.n < f2.n:
return -1
case f1.n > f2.n:
return 1
}
case f2.s == "" || f1.s > f2.s:
return 1
case f1.s < f2.s:
return -1
}
return 0
}
type list []natStr
func (l list) Len() int { return len(l) }
func (l list) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l list) Less(i, j int) bool {
ti := l[i].t
for k, t := range l[j].t {
if k == len(ti) {
return true
}
switch ti[k].Cmp(t) {
case -1:
return true
case 1:
return false
}
}
return false
}