diff --git a/mathutil/README.md b/mathutil/README.md index 71c9a7a39..4ebd14890 100644 --- a/mathutil/README.md +++ b/mathutil/README.md @@ -10,11 +10,91 @@ go get github.com/gookit/goutil/mathutil ## Go docs -- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/mathutil) +- [Go Docs](https://pkg.go.dev/github.com/gookit/goutil) ## Usage +## Functions + +```go + +func CompFloat[T comdef.Float](first, second T, op string) (ok bool) +func CompInt[T comdef.Xint](first, second T, op string) (ok bool) +func CompInt64(first, second int64, op string) bool +func CompValue[T comdef.XintOrFloat](first, second T, op string) (ok bool) +func Compare(first, second any, op string) (ok bool) +func DataSize(size uint64) string +func ElapsedTime(startTime time.Time) string +func Float(in any) (float64, error) +func FloatOr(in any, defVal float64) float64 +func FloatOrDefault(in any, defVal float64) float64 +func FloatOrErr(in any) (float64, error) +func FloatOrPanic(in any) float64 +func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T +func GteOr[T comdef.XintOrFloat](val, min, defVal T) T +func HowLongAgo(sec int64) string +func InRange[T comdef.IntOrFloat](val, min, max T) bool +func InUintRange[T comdef.Uint](val, min, max T) bool +func Int(in any) (int, error) +func Int64(in any) (int64, error) +func Int64OrErr(in any) (int64, error) +func IntOr(in any, defVal int) int +func IntOrDefault(in any, defVal int) int +func IntOrErr(in any) (iVal int, err error) +func IntOrPanic(in any) int +func IsNumeric(c byte) bool +func LessOr[T comdef.XintOrFloat](val, max, devVal T) T +func LteOr[T comdef.XintOrFloat](val, max, devVal T) T +func Max[T comdef.XintOrFloat](x, y T) T +func MaxFloat(x, y float64) float64 +func MaxI64(x, y int64) int64 +func MaxInt(x, y int) int +func Min[T comdef.XintOrFloat](x, y T) T +func MustFloat(in any) float64 +func MustInt(in any) int +func MustInt64(in any) int64 +func MustString(val any) string +func MustUint(in any) uint64 +func OrElse[T comdef.XintOrFloat](val, defVal T) T +func OutRange[T comdef.IntOrFloat](val, min, max T) bool +func Percent(val, total int) float64 +func QuietFloat(in any) float64 +func QuietInt(in any) int +func QuietInt64(in any) int64 +func QuietString(val any) string +func QuietUint(in any) uint64 +func RandInt(min, max int) int +func RandIntWithSeed(min, max int, seed int64) int +func RandomInt(min, max int) int +func RandomIntWithSeed(min, max int, seed int64) int +func SafeFloat(in any) float64 +func SafeInt(in any) int +func SafeInt64(in any) int64 +func SafeUint(in any) uint64 +func StrInt(s string) int +func StrIntOr(s string, defVal int) int +func String(val any) string +func StringOrErr(val any) (string, error) +func StringOrPanic(val any) string +func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) +func SwapMaxI64(x, y int64) (int64, int64) +func SwapMaxInt(x, y int) (int, int) +func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) +func ToFloat(in any) (f64 float64, err error) +func ToFloatWithFunc(in any, usrFn func(any) (float64, error)) (f64 float64, err error) +func ToInt(in any) (iVal int, err error) +func ToInt64(in any) (i64 int64, err error) +func ToString(val any) (string, error) +func ToUint(in any) (u64 uint64, err error) +func ToUintWithFunc(in any, usrFn func(any) (uint64, error)) (u64 uint64, err error) +func TryToString(val any, defaultAsErr bool) (str string, err error) +func Uint(in any) (uint64, error) +func UintOrErr(in any) (uint64, error) +func ZeroOr[T comdef.XintOrFloat](val, defVal T) T + +``` + ## Testings ```shell diff --git a/mathutil/check.go b/mathutil/check.go index 055fc4df0..bd876031e 100644 --- a/mathutil/check.go +++ b/mathutil/check.go @@ -2,7 +2,12 @@ package mathutil import "github.com/gookit/goutil/comdef" -// Compare any intX,floatX value by given op. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` +// IsNumeric returns true if the given character is a numeric, otherwise false. +func IsNumeric(c byte) bool { + return c >= '0' && c <= '9' +} + +// Compare any intX,floatX value by given op. returns `first op(=,!=,<,<=,>,>=) second` // // Usage: // @@ -10,70 +15,61 @@ import "github.com/gookit/goutil/comdef" // mathutil.Compare(2, 1.3, ">") // true // mathutil.Compare(2.2, 1.3, ">") // true // mathutil.Compare(2.1, 2, ">") // true -func Compare(srcVal, dstVal any, op string) (ok bool) { - if srcVal == nil || dstVal == nil { +func Compare(first, second any, op string) bool { + if first == nil || second == nil { return false } - // float - if srcFlt, ok := srcVal.(float64); ok { - if dstFlt, err := ToFloat(dstVal); err == nil { - return CompFloat(srcFlt, dstFlt, op) + switch fVal := first.(type) { + case float64: + if sVal, err := ToFloat(second); err == nil { + return CompFloat(fVal, sVal, op) } - return false - } - - if srcFlt, ok := srcVal.(float32); ok { - if dstFlt, err := ToFloat(dstVal); err == nil { - return CompFloat(float64(srcFlt), dstFlt, op) + case float32: + if sVal, err := ToFloat(second); err == nil { + return CompFloat(float64(fVal), sVal, op) + } + default: // as int64 + if int1, err := ToInt64(first); err == nil { + if int2, err := ToInt64(second); err == nil { + return CompInt64(int1, int2, op) + } } - return false - } - - // as int64 - srcInt, err := ToInt64(srcVal) - if err != nil { - return false - } - - dstInt, err := ToInt64(dstVal) - if err != nil { - return false } - return CompInt64(srcInt, dstInt, op) + return false } -// CompInt compare int,uint value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` -func CompInt[T comdef.Xint](srcVal, dstVal T, op string) (ok bool) { - return CompValue(srcVal, dstVal, op) +// CompInt compare all intX,uintX type value. returns `first op(=,!=,<,<=,>,>=) second` +func CompInt[T comdef.Xint](first, second T, op string) (ok bool) { + return CompValue(first, second, op) } -// CompInt64 compare int64 value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` -func CompInt64(srcVal, dstVal int64, op string) bool { - return CompValue(srcVal, dstVal, op) +// CompInt64 compare int64 value. returns `first op(=,!=,<,<=,>,>=) second` +func CompInt64(first, second int64, op string) bool { + return CompValue(first, second, op) } -// CompFloat compare float64,float32 value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` -func CompFloat[T comdef.Float](srcVal, dstVal T, op string) (ok bool) { - return CompValue(srcVal, dstVal, op) +// CompFloat compare float64,float32 value. returns `first op(=,!=,<,<=,>,>=) second` +func CompFloat[T comdef.Float](first, second T, op string) (ok bool) { + return CompValue(first, second, op) } -// CompValue compare intX,uintX,floatX value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` -func CompValue[T comdef.XintOrFloat](srcVal, dstVal T, op string) (ok bool) { +// CompValue compare intX,uintX,floatX value. returns `first op(=,!=,<,<=,>,>=) second` +func CompValue[T comdef.XintOrFloat](first, second T, op string) (ok bool) { switch op { case "<", "lt": - ok = srcVal < dstVal + ok = first < second case "<=", "lte": - ok = srcVal <= dstVal + ok = first <= second case ">", "gt": - ok = srcVal > dstVal + ok = first > second case ">=", "gte": - ok = srcVal >= dstVal + ok = first >= second case "=", "eq": - ok = srcVal == dstVal + ok = first == second case "!=", "ne", "neq": - ok = srcVal != dstVal + ok = first != second } return } diff --git a/mathutil/check_test.go b/mathutil/check_test.go index b4de3b40f..06f694928 100644 --- a/mathutil/check_test.go +++ b/mathutil/check_test.go @@ -8,6 +8,11 @@ import ( "github.com/gookit/goutil/testutil/assert" ) +func TestIsNumeric(t *testing.T) { + assert.True(t, mathutil.IsNumeric('3')) + assert.False(t, mathutil.IsNumeric('a')) +} + func TestCompare(t *testing.T) { tests := []struct { x, y any @@ -42,7 +47,20 @@ func TestCompare(t *testing.T) { assert.False(t, mathutil.Compare("abc", 3, comdef.OpGt)) assert.False(t, mathutil.Compare(2, "def", comdef.OpGt)) - assert.True(t, mathutil.CompInt64(2, 3, comdef.OpLt)) + // float64 + assert.False(t, mathutil.Compare(2.4, "def", comdef.OpGt)) + + // float32 + assert.True(t, mathutil.Compare(float32(2.3), float32(2.1), comdef.OpGt)) + assert.False(t, mathutil.Compare(float32(2.3), float32(2.1), "<")) + assert.False(t, mathutil.Compare(float32(2.3), "invalid", "<")) + + assert.True(t, mathutil.CompInt(2, 3, comdef.OpLt)) + + // int64 + assert.True(t, mathutil.CompInt64(int64(2), 3, comdef.OpLt)) + assert.True(t, mathutil.CompInt64(int64(22), 3, comdef.OpGt)) + assert.False(t, mathutil.CompInt64(int64(2), 3, comdef.OpGt)) } func TestInRange(t *testing.T) { diff --git a/mathutil/compare.go b/mathutil/compare.go new file mode 100644 index 000000000..f18e28a5c --- /dev/null +++ b/mathutil/compare.go @@ -0,0 +1,76 @@ +package mathutil + +import ( + "math" + + "github.com/gookit/goutil/comdef" +) + +// Min compare two value and return max value +func Min[T comdef.XintOrFloat](x, y T) T { + if x < y { + return x + } + return y +} + +// Max compare two value and return max value +func Max[T comdef.XintOrFloat](x, y T) T { + if x > y { + return x + } + return y +} + +// SwapMin compare and always return [min, max] value +func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) { + if x < y { + return x, y + } + return y, x +} + +// SwapMax compare and always return [max, min] value +func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) { + if x > y { + return x, y + } + return y, x +} + +// MaxInt compare and return max value +func MaxInt(x, y int) int { + if x > y { + return x + } + return y +} + +// SwapMaxInt compare and return max, min value +func SwapMaxInt(x, y int) (int, int) { + if x > y { + return x, y + } + return y, x +} + +// MaxI64 compare and return max value +func MaxI64(x, y int64) int64 { + if x > y { + return x + } + return y +} + +// SwapMaxI64 compare and return max, min value +func SwapMaxI64(x, y int64) (int64, int64) { + if x > y { + return x, y + } + return y, x +} + +// MaxFloat compare and return max value +func MaxFloat(x, y float64) float64 { + return math.Max(x, y) +} diff --git a/mathutil/compare_test.go b/mathutil/compare_test.go new file mode 100644 index 000000000..886c60958 --- /dev/null +++ b/mathutil/compare_test.go @@ -0,0 +1,63 @@ +package mathutil_test + +import ( + "testing" + + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/testutil/assert" +) + +func TestMaxFloat(t *testing.T) { + assert.Eq(t, float64(3), mathutil.MaxFloat(2, 3)) + assert.Eq(t, 3.3, mathutil.MaxFloat(2.1, 3.3)) + + assert.Eq(t, 3.3, mathutil.Max(2.1, 3.3)) + assert.Eq(t, 3.3, mathutil.Max(3.3, 2.1)) + + assert.Eq(t, 2.1, mathutil.Min(2.1, 3.3)) + assert.Eq(t, 2.1, mathutil.Min(3.3, 2.1)) +} + +func TestMaxI64(t *testing.T) { + assert.Eq(t, 3, mathutil.MaxInt(2, 3)) + assert.Eq(t, 3, mathutil.MaxInt(3, 2)) + + assert.Eq(t, int64(3), mathutil.MaxI64(2, 3)) + assert.Eq(t, int64(3), mathutil.MaxI64(3, 2)) + + assert.Eq(t, 3, mathutil.Max[int](3, 2)) + assert.Eq(t, int64(3), mathutil.Max[int64](3, 2)) + assert.Eq(t, int64(3), mathutil.Max(int64(3), int64(2))) +} + +func TestSwapMaxInt(t *testing.T) { + x, y := mathutil.SwapMax(2, 34) + assert.Eq(t, 34, x) + assert.Eq(t, 2, y) + + x, y = mathutil.SwapMax(34, 2) + assert.Eq(t, 34, x) + assert.Eq(t, 2, y) + + x, y = mathutil.SwapMin(2, 34) + assert.Eq(t, 2, x) + assert.Eq(t, 34, y) + + x, y = mathutil.SwapMin(34, 2) + assert.Eq(t, 2, x) + assert.Eq(t, 34, y) + + x, y = mathutil.SwapMaxInt(2, 34) + assert.Eq(t, 34, x) + assert.Eq(t, 2, y) + x, y = mathutil.SwapMaxInt(34, 2) + assert.Eq(t, 34, x) + assert.Eq(t, 2, y) + + x64, y64 := mathutil.SwapMaxI64(2, 34) + assert.Eq(t, int64(34), x64) + assert.Eq(t, int64(2), y64) + x64, y64 = mathutil.SwapMaxI64(34, 2) + assert.Eq(t, int64(34), x64) + assert.Eq(t, int64(2), y64) +} diff --git a/mathutil/convert.go b/mathutil/convert.go index 4778cb90a..f1c756cc4 100644 --- a/mathutil/convert.go +++ b/mathutil/convert.go @@ -11,6 +11,18 @@ import ( "github.com/gookit/goutil/comdef" ) +// ToIntFunc convert value to int +type ToIntFunc func(any) (int, error) + +// ToInt64Func convert value to int64 +type ToInt64Func func(any) (int64, error) + +// ToUintFunc convert value to uint +type ToUintFunc func(any) (uint64, error) + +// ToFloatFunc convert value to float +type ToFloatFunc func(any) (float64, error) + /************************************************************* * convert value to int *************************************************************/ @@ -28,8 +40,7 @@ func SafeInt(in any) int { // QuietInt convert value to int, will ignore error func QuietInt(in any) int { - val, _ := ToInt(in) - return val + return SafeInt(in) } // MustInt convert value to int, will panic on error @@ -43,23 +54,36 @@ func MustInt(in any) int { // IntOrPanic convert value to int, will panic on error func IntOrPanic(in any) int { - val, err := ToInt(in) + return MustInt(in) +} + +// IntOrDefault convert value to int, return defaultVal on failed +func IntOrDefault(in any, defVal int) int { + return IntOr(in, defVal) +} + +// IntOr convert value to int, return defaultVal on failed +func IntOr(in any, defVal int) int { + val, err := ToIntWithFunc(in, nil) if err != nil { - panic(err) + return defVal } return val } // IntOrErr convert value to int, return error on failed func IntOrErr(in any) (iVal int, err error) { - return ToInt(in) + return ToIntWithFunc(in, nil) } // ToInt convert value to int, return error on failed func ToInt(in any) (iVal int, err error) { + return ToIntWithFunc(in, nil) +} + +// ToIntWithFunc convert value to int, will call usrFn on value type not supported. +func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) { switch tVal := in.(type) { - case nil: - iVal = 0 case int: iVal = tVal case int8: @@ -118,7 +142,11 @@ func ToInt(in any) (iVal int, err error) { } } default: - err = comdef.ErrConvType + if usrFn != nil { + return usrFn(in) + } else { + err = comdef.ErrConvType + } } return } @@ -129,46 +157,71 @@ func StrInt(s string) int { return iVal } +// StrIntOr convert string to int, return default val on failed +func StrIntOr(s string, defVal int) int { + iVal, err := strconv.Atoi(strings.TrimSpace(s)) + if err != nil { + return defVal + } + return iVal +} + /************************************************************* * convert value to uint *************************************************************/ -// Uint convert string to uint, return error on failed +// Uint convert any to uint, return error on failed func Uint(in any) (uint64, error) { return ToUint(in) } -// SafeUint convert string to uint, will ignore error +// SafeUint convert any to uint, will ignore error func SafeUint(in any) uint64 { val, _ := ToUint(in) return val } -// QuietUint convert string to uint, will ignore error +// QuietUint convert any to uint, will ignore error func QuietUint(in any) uint64 { - val, _ := ToUint(in) - return val + return SafeUint(in) } -// MustUint convert string to uint, will panic on error +// MustUint convert any to uint, will panic on error func MustUint(in any) uint64 { - val, err := ToUint(in) + val, err := ToUintWithFunc(in, nil) if err != nil { panic(err) } return val } +// UintOrDefault convert any to uint, return default val on failed +func UintOrDefault(in any, defVal uint64) uint64 { + return UintOr(in, defVal) +} + +// UintOr convert any to uint, return default val on failed +func UintOr(in any, defVal uint64) uint64 { + val, err := ToUintWithFunc(in, nil) + if err != nil { + return defVal + } + return val +} + // UintOrErr convert value to uint, return error on failed func UintOrErr(in any) (uint64, error) { - return ToUint(in) + return ToUintWithFunc(in, nil) } // ToUint convert value to uint, return error on failed func ToUint(in any) (u64 uint64, err error) { + return ToUintWithFunc(in, nil) +} + +// ToUintWithFunc convert value to uint, will call usrFn on value type not supported. +func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) { switch tVal := in.(type) { - case nil: - u64 = 0 case int: u64 = uint64(tVal) case int8: @@ -202,12 +255,11 @@ func ToUint(in any) (u64 uint64, err error) { case string: u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0) default: - // if iface, ok := in.(comdef.Int64able); ok { - // var i64 int64 - // i64, err = iface.Int64() - // u64 = uint64(i64) - // } else { - err = comdef.ErrConvType + if usrFn != nil { + u64, err = usrFn(in) + } else { + err = comdef.ErrConvType + } } return } @@ -216,44 +268,58 @@ func ToUint(in any) (u64 uint64, err error) { * convert value to int64 *************************************************************/ -// Int64 convert string to int64, return error on failed +// Int64 convert value to int64, return error on failed func Int64(in any) (int64, error) { return ToInt64(in) } // SafeInt64 convert value to int64, will ignore error func SafeInt64(in any) int64 { - i64, _ := ToInt64(in) + i64, _ := ToInt64WithFunc(in, nil) return i64 } // QuietInt64 convert value to int64, will ignore error func QuietInt64(in any) int64 { - i64, _ := ToInt64(in) - return i64 + return SafeInt64(in) } // MustInt64 convert value to int64, will panic on error func MustInt64(in any) int64 { - i64, err := ToInt64(in) + i64, err := ToInt64WithFunc(in, nil) if err != nil { panic(err) } return i64 } -// TODO AsInt64 strict convert to int64 +// Int64OrDefault convert value to int64, return default val on failed +func Int64OrDefault(in any, defVal int64) int64 { + return Int64Or(in, defVal) +} + +// Int64Or convert value to int64, return default val on failed +func Int64Or(in any, defVal int64) int64 { + i64, err := ToInt64WithFunc(in, nil) + if err != nil { + return defVal + } + return i64 +} -// Int64OrErr convert string to int64, return error on failed +// Int64OrErr convert value to int64, return error on failed func Int64OrErr(in any) (int64, error) { return ToInt64(in) } -// ToInt64 convert string to int64, return error on failed +// ToInt64 convert value to int64, return error on failed func ToInt64(in any) (i64 int64, err error) { + return ToInt64WithFunc(in, nil) +} + +// ToInt64WithFunc convert value to int64, will call usrFn on value type not supported. +func ToInt64WithFunc(in any, usrFn ToInt64Func) (i64 int64, err error) { switch tVal := in.(type) { - case nil: - i64 = 0 case string: i64, err = strconv.ParseInt(strings.TrimSpace(tVal), 10, 0) case int: @@ -285,7 +351,11 @@ func ToInt64(in any) (i64 int64, err error) { case json.Number: i64, err = tVal.Int64() default: - err = comdef.ErrConvType + if usrFn != nil { + i64, err = usrFn(in) + } else { + err = comdef.ErrConvType + } } return } @@ -294,45 +364,63 @@ func ToInt64(in any) (i64 int64, err error) { * convert value to float *************************************************************/ -// QuietFloat convert value to float64, will ignore error +// QuietFloat convert value to float64, will ignore error. alias of SafeFloat func QuietFloat(in any) float64 { - val, _ := ToFloat(in) + return SafeFloat(in) +} + +// SafeFloat convert value to float64, will ignore error +func SafeFloat(in any) float64 { + val, _ := ToFloatWithFunc(in, nil) return val } // FloatOrPanic convert value to float64, will panic on error func FloatOrPanic(in any) float64 { - val, err := ToFloat(in) + return MustFloat(in) +} + +// MustFloat convert value to float64, will panic on error +func MustFloat(in any) float64 { + val, err := ToFloatWithFunc(in, nil) if err != nil { panic(err) } return val } -// MustFloat convert value to float64, will panic on error -func MustFloat(in any) float64 { - val, err := ToFloat(in) +// FloatOrDefault convert value to float64, will return default value on error +func FloatOrDefault(in any, defVal float64) float64 { + return FloatOr(in, defVal) +} + +// FloatOr convert value to float64, will return default value on error +func FloatOr(in any, defVal float64) float64 { + val, err := ToFloatWithFunc(in, nil) if err != nil { - panic(err) + return defVal } return val } // Float convert value to float64, return error on failed func Float(in any) (float64, error) { - return ToFloat(in) + return ToFloatWithFunc(in, nil) } // FloatOrErr convert value to float64, return error on failed func FloatOrErr(in any) (float64, error) { - return ToFloat(in) + return ToFloatWithFunc(in, nil) } // ToFloat convert value to float64, return error on failed func ToFloat(in any) (f64 float64, err error) { + return ToFloatWithFunc(in, nil) +} + +// ToFloatWithFunc convert value to float64, will call usrFn if value type not supported. +func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) { switch tVal := in.(type) { - case nil: - f64 = 0 case string: f64, err = strconv.ParseFloat(strings.TrimSpace(tVal), 64) case int: @@ -364,7 +452,11 @@ func ToFloat(in any) (f64 float64, err error) { case json.Number: f64, err = tVal.Float64() default: - err = comdef.ErrConvType + if usrFn != nil { + f64, err = usrFn(in) + } else { + err = comdef.ErrConvType + } } return } @@ -373,28 +465,45 @@ func ToFloat(in any) (f64 float64, err error) { * convert intX/floatX to string *************************************************************/ -// StringOrPanic convert intX/floatX value to string, will panic on error -func StringOrPanic(val any) string { - str, err := TryToString(val, true) +// MustString convert intX/floatX value to string, will panic on error +func MustString(val any) string { + str, err := ToStringWithFunc(val, nil) if err != nil { panic(err) } return str } -// MustString convert intX/floatX value to string, will panic on error -func MustString(val any) string { return StringOrPanic(val) } +// StringOrPanic convert intX/floatX value to string, will panic on error +func StringOrPanic(val any) string { return MustString(val) } + +// StringOrDefault convert intX/floatX value to string, will return default value on error +func StringOrDefault(val any, defVal string) string { + return StringOr(val, defVal) +} + +// StringOr convert intX/floatX value to string, will return default value on error +func StringOr(val any, defVal string) string { + str, err := ToStringWithFunc(val, nil) + if err != nil { + return defVal + } + return str +} // ToString convert intX/floatX value to string, return error on failed -func ToString(val any) (string, error) { return TryToString(val, true) } +func ToString(val any) (string, error) { + return ToStringWithFunc(val, nil) +} // StringOrErr convert intX/floatX value to string, return error on failed -func StringOrErr(val any) (string, error) { return TryToString(val, true) } +func StringOrErr(val any) (string, error) { + return ToStringWithFunc(val, nil) +} // QuietString convert intX/floatX value to string, other type convert by fmt.Sprint func QuietString(val any) string { - str, _ := TryToString(val, false) - return str + return SafeString(val) } // String convert intX/floatX value to string, other type convert by fmt.Sprint @@ -403,14 +512,33 @@ func String(val any) string { return str } +// SafeString convert intX/floatX value to string, other type convert by fmt.Sprint +func SafeString(val any) string { + str, _ := TryToString(val, false) + return str +} + // TryToString try convert intX/floatX value to string // // if defaultAsErr is False, will use fmt.Sprint convert other type func TryToString(val any, defaultAsErr bool) (str string, err error) { - if val == nil { - return + var usrFn comdef.ToStringFunc + if !defaultAsErr { + usrFn = func(v any) (string, error) { + if val == nil { + return "", nil + } + return fmt.Sprint(v), nil + } } + return ToStringWithFunc(val, usrFn) +} + +// ToStringWithFunc try convert intX/floatX value to string, will call usrFn if value type not supported. +// +// if defaultAsErr is False, will use fmt.Sprint convert other type +func ToStringWithFunc(val any, usrFn comdef.ToStringFunc) (str string, err error) { switch value := val.(type) { case int: str = strconv.Itoa(value) @@ -438,14 +566,31 @@ func TryToString(val any, defaultAsErr bool) (str string, err error) { str = strconv.FormatFloat(value, 'f', -1, 64) case time.Duration: str = strconv.FormatInt(int64(value), 10) + case string: + str = value case fmt.Stringer: str = value.String() default: - if defaultAsErr { - err = comdef.ErrConvType + if usrFn != nil { + str, err = usrFn(val) } else { - str = fmt.Sprint(value) + err = comdef.ErrConvType } } return } + +// Percent returns a values percent of the total +func Percent(val, total int) float64 { + if total == 0 { + return float64(0) + } + return (float64(val) / float64(total)) * 100 +} + +// ElapsedTime calc elapsed time 计算运行时间消耗 单位 ms(毫秒) +// +// Deprecated: use timex.ElapsedTime() +func ElapsedTime(startTime time.Time) string { + return fmt.Sprintf("%.3f", time.Since(startTime).Seconds()*1000) +} diff --git a/mathutil/convert_test.go b/mathutil/convert_test.go index 5c1d37a8a..4f6292196 100644 --- a/mathutil/convert_test.go +++ b/mathutil/convert_test.go @@ -2,6 +2,7 @@ package mathutil_test import ( "encoding/json" + "errors" "math" "testing" "time" @@ -47,7 +48,8 @@ func TestToInt(t *testing.T) { is.Nil(err) is.Eq(-2, intVal) - is.Eq(2, mathutil.StrInt("2")) + _, err = mathutil.ToInt(nil) + is.Err(err) intVal, err = mathutil.IntOrErr("-2") is.Nil(err) @@ -56,6 +58,8 @@ func TestToInt(t *testing.T) { is.Eq(0, mathutil.SafeInt(nil)) is.Eq(-2, mathutil.MustInt("-2")) is.Eq(-2, mathutil.IntOrPanic("-2")) + is.Eq(2, mathutil.IntOrDefault("invalid", 2)) + for _, in := range tests { is.Eq(2, mathutil.MustInt(in)) is.Eq(2, mathutil.QuietInt(in)) @@ -87,6 +91,8 @@ func TestToInt(t *testing.T) { is.Nil(err) is.Eq(uint64(2), uintVal) + _, err = mathutil.ToUint(nil) + is.Err(err) _, err = mathutil.ToUint("-2") is.Err(err) @@ -100,6 +106,7 @@ func TestToInt(t *testing.T) { is.Eq(uint64(0), mathutil.QuietUint(nil)) is.Eq(uint64(2), mathutil.MustUint("2")) + is.Eq(uint64(2), mathutil.UintOrDefault("invalid", 2)) is.Panics(func() { mathutil.MustUint([]int{23}) }) @@ -128,12 +135,23 @@ func TestToInt(t *testing.T) { } is.Eq(int64(0), mathutil.QuietInt64(nil)) + is.Eq(int64(2), mathutil.Int64OrDefault("invalid", 2)) is.Panics(func() { mathutil.MustInt64([]int{23}) }) }) } +func TestStrInt(t *testing.T) { + is := assert.New(t) + + is.Eq(2, mathutil.StrInt("2")) + is.Eq(0, mathutil.StrInt("2a")) + // StrIntOr + is.Eq(2, mathutil.StrIntOr("2", 3)) + is.Eq(3, mathutil.StrIntOr("2a", 3)) +} + func TestToString(t *testing.T) { is := assert.New(t) @@ -160,14 +178,16 @@ func TestToString(t *testing.T) { is.NoErr(err) is.Eq("2", val) - val, err = mathutil.ToString(nil) - is.NoErr(err) - is.Eq("", val) + _, err = mathutil.ToString(nil) + is.Err(err) + is.Eq("", mathutil.SafeString(nil)) is.Eq("[1]", mathutil.QuietString([]int{1})) + is.Eq("23", mathutil.StringOrDefault(nil, "23")) + is.Eq("23", mathutil.StringOr("23", "2")) is.Panics(func() { - mathutil.MustString("2") + mathutil.StringOrPanic([]int{23}) }) } @@ -194,6 +214,10 @@ func TestToFloat(t *testing.T) { is.Eq(float64(0), mathutil.QuietFloat("invalid")) is.Eq(float64(0), mathutil.QuietFloat([]int{23})) + // FloatOrDefault + is.Eq(123.5, mathutil.FloatOrDefault("invalid", 123.5)) + is.Eq(123.1, mathutil.FloatOr(123.1, 123.5)) + is.Panics(func() { mathutil.MustFloat("invalid") }) @@ -212,4 +236,23 @@ func TestToFloat(t *testing.T) { fltVal, err = mathutil.FloatOrErr("-123.5") is.Nil(err) is.Eq(-123.5, fltVal) + + // ToFloatWithFunc + _, err = mathutil.ToFloatWithFunc([]int{2}, func(v any) (float64, error) { + return 0, errors.New("invalid") + }) + is.ErrMsg(err, "invalid") +} + +func TestPercent(t *testing.T) { + assert.Eq(t, float64(34), mathutil.Percent(34, 100)) + assert.Eq(t, float64(0), mathutil.Percent(34, 0)) + assert.Eq(t, float64(-100), mathutil.Percent(34, -34)) +} + +func TestElapsedTime(t *testing.T) { + nt := time.Now().Add(-time.Second * 3) + num := mathutil.ElapsedTime(nt) + + assert.Eq(t, 3000, int(mathutil.MustFloat(num))) } diff --git a/mathutil/mathutil.go b/mathutil/mathutil.go index 52a2e145d..1b47f32a9 100644 --- a/mathutil/mathutil.go +++ b/mathutil/mathutil.go @@ -2,84 +2,72 @@ package mathutil import ( - "math" - "github.com/gookit/goutil/comdef" ) -// Min compare two value and return max value -func Min[T comdef.XintOrFloat](x, y T) T { - if x < y { - return x - } - return y +// OrElse return default value on val is zero, else return val +func OrElse[T comdef.XintOrFloat](val, defVal T) T { + return ZeroOr(val, defVal) } -// Max compare two value and return max value -func Max[T comdef.XintOrFloat](x, y T) T { - if x > y { - return x +// ZeroOr return default value on val is zero, else return val +func ZeroOr[T comdef.XintOrFloat](val, defVal T) T { + if val != 0 { + return val } - return y + return defVal } -// SwapMin compare and always return [min, max] value -func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) { - if x < y { - return x, y +// LessOr return val on val < max, else return default value. +// +// Example: +// +// LessOr(11, 10, 1) // 1 +// LessOr(2, 10, 1) // 2 +// LessOr(10, 10, 1) // 1 +func LessOr[T comdef.XintOrFloat](val, max, devVal T) T { + if val < max { + return val } - return y, x + return devVal } -// SwapMax compare and always return [max, min] value -func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) { - if x > y { - return x, y +// LteOr return val on val <= max, else return default value. +// +// Example: +// +// LteOr(11, 10, 1) // 11 +// LteOr(2, 10, 1) // 2 +// LteOr(10, 10, 1) // 10 +func LteOr[T comdef.XintOrFloat](val, max, devVal T) T { + if val <= max { + return val } - return y, x + return devVal } -// MaxInt compare and return max value -func MaxInt(x, y int) int { - if x > y { - return x +// GreaterOr return val on val > max, else return default value. +// +// Example: +// +// GreaterOr(23, 0, 2) // 23 +// GreaterOr(0, 0, 2) // 2 +func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T { + if val > min { + return val } - return y -} - -// SwapMaxInt compare and return max, min value -func SwapMaxInt(x, y int) (int, int) { - if x > y { - return x, y - } - return y, x -} - -// MaxI64 compare and return max value -func MaxI64(x, y int64) int64 { - if x > y { - return x - } - return y -} - -// SwapMaxI64 compare and return max, min value -func SwapMaxI64(x, y int64) (int64, int64) { - if x > y { - return x, y - } - return y, x -} - -// MaxFloat compare and return max value -func MaxFloat(x, y float64) float64 { - return math.Max(x, y) + return defVal } -// OrElse return s OR nv(new-value) on s is empty -func OrElse[T comdef.XintOrFloat](in, nv T) T { - if in != 0 { - return in +// GteOr return val on val >= max, else return default value. +// +// Example: +// +// GteOr(23, 0, 2) // 23 +// GteOr(0, 0, 2) // 0 +func GteOr[T comdef.XintOrFloat](val, min, defVal T) T { + if val >= min { + return val } - return nv + return defVal } diff --git a/mathutil/mathutil_test.go b/mathutil/mathutil_test.go index f3f80e90a..66164ac13 100644 --- a/mathutil/mathutil_test.go +++ b/mathutil/mathutil_test.go @@ -7,56 +7,29 @@ import ( "github.com/gookit/goutil/testutil/assert" ) -func TestMaxFloat(t *testing.T) { - assert.Eq(t, float64(3), mathutil.MaxFloat(2, 3)) - assert.Eq(t, 3.3, mathutil.MaxFloat(2.1, 3.3)) - - assert.Eq(t, 3.3, mathutil.Max(2.1, 3.3)) - assert.Eq(t, 3.3, mathutil.Max(3.3, 2.1)) - - assert.Eq(t, 2.1, mathutil.Min(2.1, 3.3)) - assert.Eq(t, 2.1, mathutil.Min(3.3, 2.1)) +func TestOrElse(t *testing.T) { + assert.Eq(t, 23, mathutil.OrElse(23, 21)) + assert.Eq(t, 21.3, mathutil.OrElse[float64](0, 21.3)) } -func TestMaxI64(t *testing.T) { - assert.Eq(t, 3, mathutil.MaxInt(2, 3)) - assert.Eq(t, 3, mathutil.MaxInt(3, 2)) - - assert.Eq(t, int64(3), mathutil.MaxI64(2, 3)) - assert.Eq(t, int64(3), mathutil.MaxI64(3, 2)) +func TestLessOr(t *testing.T) { + assert.Eq(t, 23, mathutil.LessOr(23, 25, 0)) + assert.Eq(t, 11, mathutil.LessOr(23, 21, 11)) + assert.Eq(t, 11, mathutil.LessOr(21, 21, 11)) - assert.Eq(t, 3, mathutil.Max[int](3, 2)) - assert.Eq(t, int64(3), mathutil.Max[int64](3, 2)) - assert.Eq(t, int64(3), mathutil.Max(int64(3), int64(2))) + // LteOr + assert.Eq(t, 23, mathutil.LteOr(23, 25, 0)) + assert.Eq(t, 11, mathutil.LteOr(23, 21, 11)) + assert.Eq(t, 21, mathutil.LteOr(21, 21, 11)) } -func TestSwapMaxInt(t *testing.T) { - x, y := mathutil.SwapMax(2, 34) - assert.Eq(t, 34, x) - assert.Eq(t, 2, y) - - x, y = mathutil.SwapMax(34, 2) - assert.Eq(t, 34, x) - assert.Eq(t, 2, y) - - x, y = mathutil.SwapMin(2, 34) - assert.Eq(t, 2, x) - assert.Eq(t, 34, y) +func TestGreaterOr(t *testing.T) { + assert.Eq(t, 23, mathutil.GreaterOr(23, 21, 0)) + assert.Eq(t, 21, mathutil.GreaterOr(23, 25, 21)) + assert.Eq(t, 11, mathutil.GreaterOr(21, 21, 11)) - x, y = mathutil.SwapMin(34, 2) - assert.Eq(t, 2, x) - assert.Eq(t, 34, y) - - x, y = mathutil.SwapMaxInt(2, 34) - assert.Eq(t, 34, x) - assert.Eq(t, 2, y) - - x64, y64 := mathutil.SwapMaxI64(2, 34) - assert.Eq(t, int64(34), x64) - assert.Eq(t, int64(2), y64) -} - -func TestOrElse(t *testing.T) { - assert.Eq(t, 23, mathutil.OrElse(23, 21)) - assert.Eq(t, 21.3, mathutil.OrElse[float64](0, 21.3)) + // GteOr + assert.Eq(t, 23, mathutil.GteOr(23, 21, 0)) + assert.Eq(t, 21, mathutil.GteOr(23, 25, 21)) + assert.Eq(t, 21, mathutil.GteOr(21, 21, 11)) } diff --git a/mathutil/number.go b/mathutil/number.go deleted file mode 100644 index d26cbbb5d..000000000 --- a/mathutil/number.go +++ /dev/null @@ -1,26 +0,0 @@ -package mathutil - -import ( - "fmt" - "time" -) - -// IsNumeric returns true if the given character is a numeric, otherwise false. -func IsNumeric(c byte) bool { - return c >= '0' && c <= '9' -} - -// Percent returns a values percent of the total -func Percent(val, total int) float64 { - if total == 0 { - return float64(0) - } - return (float64(val) / float64(total)) * 100 -} - -// ElapsedTime calc elapsed time 计算运行时间消耗 单位 ms(毫秒) -// -// Deprecated: use timex.ElapsedTime() -func ElapsedTime(startTime time.Time) string { - return fmt.Sprintf("%.3f", time.Since(startTime).Seconds()*1000) -} diff --git a/mathutil/number_test.go b/mathutil/number_test.go deleted file mode 100644 index c3cb2f8f4..000000000 --- a/mathutil/number_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package mathutil_test - -import ( - "testing" - "time" - - "github.com/gookit/goutil/mathutil" - "github.com/gookit/goutil/testutil/assert" -) - -func TestIsNumeric(t *testing.T) { - assert.True(t, mathutil.IsNumeric('3')) - assert.False(t, mathutil.IsNumeric('a')) -} - -func TestPercent(t *testing.T) { - assert.Eq(t, float64(34), mathutil.Percent(34, 100)) - assert.Eq(t, float64(0), mathutil.Percent(34, 0)) - assert.Eq(t, float64(-100), mathutil.Percent(34, -34)) -} - -func TestElapsedTime(t *testing.T) { - nt := time.Now().Add(-time.Second * 3) - num := mathutil.ElapsedTime(nt) - - assert.Eq(t, 3000, int(mathutil.MustFloat(num))) -} diff --git a/mathutil/random_test.go b/mathutil/random_test.go index 88a16e2d3..6aff13a9a 100644 --- a/mathutil/random_test.go +++ b/mathutil/random_test.go @@ -24,4 +24,5 @@ func TestRandomInt(t *testing.T) { } assert.True(t, mathutil.RandInt(min, max) > 999) + assert.True(t, mathutil.RandIntWithSeed(min, max, 23) > 999) }