pkg.re/essentialkaos/ek.v11@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 }