gob/compare/compare.go

161 lines
3.9 KiB
Go
Raw Permalink Normal View History

2023-09-23 19:57:46 +00:00
package compare
import (
"regexp"
"strings"
"gitea.zaclys.com/bvaudour/gob/unidecode"
)
var (
digit = regexp.MustCompile(`[^0-9]+|[0-9]+`)
ToLower = strings.ToLower
ToAscii = unidecode.String
)
func isNumber(input uint8) bool {
return input >= '0' && input <= '9'
}
// CompareFunc représente une fonction de comparaison de deux chaînes.
type CompareFunc func(string, string) int
// TransformFunc représente une fonction de transformation de chaîne.
type TransformFunc func(string) string
// BuildCompare retourne une fonction de comparaison à partir
// dun ensemble de comparaison à appliquer.
func BuildCompare(cmps ...CompareFunc) CompareFunc {
return func(e1, e2 string) int {
for _, cmp := range cmps {
if c := cmp(e1, e2); c != 0 {
return c
}
}
return 0
}
}
// BuildTransform retourne une fonction de transformation à partir
// de transformations successives.
func BuildTransform(transforms ...TransformFunc) TransformFunc {
return func(e string) string {
for _, t := range transforms {
e = t(e)
}
return e
}
}
func compareT(e1, e2 string, cb TransformFunc) int {
return strings.Compare(cb(e1), cb(e2))
}
// Compare compare deux chaînes après transformation.
func Compare(e1, e2 string, t ...TransformFunc) int {
return compareT(e1, e2, BuildTransform(t...))
}
// CompareInsensitive compare deux chaîne sans tenir compte de la casse.
func CompareInsensitive(e1, e2 string) int {
return Compare(e1, e2, ToLower)
}
// CompareAscii compare deux chaînes sans tenir compte des accents.
func CompareAscii(e1, e2 string) int {
return Compare(e1, e2, ToAscii)
}
// CompareAscii compare deux chaînes sans tenir compte des accents ni de la casse.
func CompareAsciiInsensitive(e1, e2 string) int {
return Compare(e1, e2, ToAscii, ToLower)
}
func naturalT(e1, e2 string, cb TransformFunc) int {
e1, e2 = cb(e1), cb(e2)
sl1 := digit.FindAllString(strings.Replace(e1, " ", "", -1), -1)
sl2 := digit.FindAllString(strings.Replace(e2, " ", "", -1), -1)
l := len(sl1)
if len(sl2) < l {
l = len(sl2)
}
for i, s1 := range sl1[:l] {
s2 := sl2[i]
// Both parts are equal
if s1 == s2 {
continue
}
n1, n2 := isNumber(s1[0]), isNumber(s2[0])
switch {
case n1 && n2:
s1 = strings.TrimLeft(s1, "0")
s2 = strings.TrimLeft(s2, "0")
return Compare(s1, s2)
case n1:
return -1
case n2:
return 1
}
return Compare(s1, s2)
}
l = len(e1)
if len(e2) < len(e1) {
l = len(e2)
}
// 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 i, b1 := range []byte(e1[:l]) {
b2 := e2[i]
switch {
case isNumber(b1):
return -1
case isNumber(b2):
return 1
}
}
return Compare(e1, e2)
}
// Natural compare des chaînes de façon “naturelle” (ie. en analysant
// les nombres éventuels compris dans la chaîne).
func Natural(e1, e2 string, t ...TransformFunc) int {
return naturalT(e1, e2, BuildTransform(t...))
}
// NaturalInsensitive compare des chaînes de manière naturelle sans tenir
// compte de la casse.
func NaturalInsensitive(e1, e2 string) int {
return Natural(e1, e2, ToLower)
}
// NaturalAscii compare des chaînes de manière naturelle sans tenir
// compte des accents.
func NaturalAscii(e1, e2 string) int {
return Natural(e1, e2, ToAscii)
}
// NaturalAsciiInsensitive compare des chaînes de manière naturelle sans tenir
// compte de la casse ni des accents.
func NaturalAsciiInsensitive(e1, e2 string) int {
return Natural(e1, e2, ToAscii, ToLower)
}
// Less transforme une succession de fonctions de comparaison
// en <.
func Less(cmps ...CompareFunc) func(string, string) bool {
return func(e1, e2 string) bool {
return BuildCompare(cmps...)(e1, e2) < 0
}
}
// Equal transforme une succession de fonctions de comparaison
// en =.
func Equal(cmps ...CompareFunc) func(string, string) bool {
return func(e1, e2 string) bool {
return BuildCompare(cmps...)(e1, e2) == 0
}
}