pkg.re/essentialkaos/ek.10@v12.41.0+incompatible/sortutil/natural.go (about)

     1  package sortutil
     2  
     3  // ////////////////////////////////////////////////////////////////////////////////// //
     4  //                                                                                    //
     5  //                         Copyright (c) 2022 ESSENTIAL KAOS                          //
     6  //      Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>     //
     7  //                                                                                    //
     8  // ////////////////////////////////////////////////////////////////////////////////// //
     9  
    10  import (
    11  	"sort"
    12  )
    13  
    14  // ////////////////////////////////////////////////////////////////////////////////// //
    15  
    16  type naturalSlice []string
    17  
    18  func (s naturalSlice) Len() int           { return len(s) }
    19  func (s naturalSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    20  func (s naturalSlice) Less(i, j int) bool { return NaturalLess(s[i], s[j]) }
    21  
    22  // ////////////////////////////////////////////////////////////////////////////////// //
    23  
    24  // StringsNatural sorts a slice of strings in natural order
    25  // Limitation: only ASCII digits (0-9) are considered.
    26  func StringsNatural(a []string) {
    27  	sort.Sort(naturalSlice(a))
    28  }
    29  
    30  // NaturalLess compares two strings using natural ordering. This means that e.g.
    31  // "abc2" < "abc12"
    32  // This code based on sortorder package created by @fvbommel
    33  func NaturalLess(s1, s2 string) bool {
    34  	i1, i2 := 0, 0
    35  	l1, l2 := len(s1), len(s2)
    36  
    37  	for i1 < l1 && i2 < l2 {
    38  		c1, c2 := s1[i1], s2[i2]
    39  		d1, d2 := isDigit(c1), isDigit(c2)
    40  
    41  		if d1 != d2 {
    42  			return d1
    43  		} else if !d1 {
    44  			if c1 != c2 {
    45  				return c1 < c2
    46  			}
    47  
    48  			i1++
    49  			i2++
    50  
    51  			continue
    52  		}
    53  
    54  		for i1 < l1 && s1[i1] == '0' {
    55  			i1++
    56  		}
    57  
    58  		for i2 < l2 && s2[i2] == '0' {
    59  			i2++
    60  		}
    61  
    62  		n1, n2 := i1, i2
    63  
    64  		for i1 < l1 && isDigit(s1[i1]) {
    65  			i1++
    66  		}
    67  
    68  		for i2 < l2 && isDigit(s2[i2]) {
    69  			i2++
    70  		}
    71  
    72  		ln1, ln2 := i1-n1, i2-n2
    73  
    74  		if ln1 != ln2 {
    75  			return ln1 < ln2
    76  		}
    77  
    78  		nl1, nl2 := s1[n1:i1], s2[n2:i2]
    79  
    80  		if nl1 != nl2 {
    81  			return nl1 < nl2
    82  		}
    83  	}
    84  
    85  	return l1 < l2
    86  }
    87  
    88  // ////////////////////////////////////////////////////////////////////////////////// //
    89  
    90  func isDigit(b byte) bool {
    91  	switch b {
    92  	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
    93  		return true
    94  	default:
    95  		return false
    96  	}
    97  }