github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/cmd/juju/common/naturalsort.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"unicode"
    12  )
    13  
    14  // SortStringsNaturally sorts strings according to their natural sort order.
    15  func SortStringsNaturally(s []string) []string {
    16  	sort.Sort(naturally(s))
    17  	return s
    18  }
    19  
    20  type naturally []string
    21  
    22  func (n naturally) Len() int {
    23  	return len(n)
    24  }
    25  
    26  func (n naturally) Swap(a, b int) {
    27  	n[a], n[b] = n[b], n[a]
    28  }
    29  
    30  // Less sorts by non-numeric prefix and numeric suffix
    31  // when one exists.
    32  func (n naturally) Less(a, b int) bool {
    33  	aPrefix, aNumber := splitAtNumber(n[a])
    34  	bPrefix, bNumber := splitAtNumber(n[b])
    35  	if aPrefix == bPrefix {
    36  		return aNumber < bNumber
    37  	}
    38  	return n[a] < n[b]
    39  }
    40  
    41  // splitAtNumber splits given string into prefix and numeric suffix.
    42  // If no numeric suffix exists, full original string is returned as
    43  // prefix with -1 as a suffix.
    44  func splitAtNumber(str string) (string, int) {
    45  	i := strings.LastIndexFunc(str, func(r rune) bool {
    46  		return !unicode.IsDigit(r)
    47  	}) + 1
    48  	if i == len(str) {
    49  		// no numeric suffix
    50  		return str, -1
    51  	}
    52  	n, err := strconv.Atoi(str[i:])
    53  	if err != nil {
    54  		panic(fmt.Sprintf("parsing number %v: %v", str[i:], err)) // should never happen
    55  	}
    56  	return str[:i], n
    57  }