161 lines
3.9 KiB
Go
161 lines
3.9 KiB
Go
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
|
||
// d’un 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
|
||
}
|
||
}
|