gob/compare/compare.go

161 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}