mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Enhanced min and max built-in functions to Support Arrays of Integers and Floats (#576)
* Enhanced min and max built-in functions to Support Arrays of Integers and Floats * Enhanced min and max built-in functions to Support Arrays of Integers and Floats
This commit is contained in:
committed by
GitHub
parent
14d3c281b2
commit
2f8f25a68d
+33
-18
@@ -394,38 +394,53 @@ var Builtins = []*Function{
|
||||
Name: "max",
|
||||
Func: Max,
|
||||
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
||||
if len(args) == 0 {
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return anyType, fmt.Errorf("not enough arguments to call max")
|
||||
}
|
||||
for _, arg := range args {
|
||||
switch kind(arg) {
|
||||
case reflect.Interface:
|
||||
case 1:
|
||||
if kindName := kind(args[0]); kindName == reflect.Array || kindName == reflect.Slice {
|
||||
return anyType, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||
default:
|
||||
return anyType, fmt.Errorf("invalid argument for max (type %s)", arg)
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
for _, arg := range args {
|
||||
switch kind(arg) {
|
||||
case reflect.Interface, reflect.Array, reflect.Slice:
|
||||
return anyType, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||
default:
|
||||
return anyType, fmt.Errorf("invalid argument for max (type %s)", arg)
|
||||
}
|
||||
}
|
||||
return args[0], nil
|
||||
}
|
||||
return args[0], nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "min",
|
||||
Func: Min,
|
||||
Validate: func(args []reflect.Type) (reflect.Type, error) {
|
||||
if len(args) == 0 {
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return anyType, fmt.Errorf("not enough arguments to call min")
|
||||
}
|
||||
for _, arg := range args {
|
||||
switch kind(arg) {
|
||||
case reflect.Interface:
|
||||
case 1:
|
||||
if kindName := kind(args[0]); kindName == reflect.Array || kindName == reflect.Slice {
|
||||
return anyType, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||
default:
|
||||
return anyType, fmt.Errorf("invalid argument for min (type %s)", arg)
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
for _, arg := range args {
|
||||
switch kind(arg) {
|
||||
case reflect.Interface, reflect.Array, reflect.Slice:
|
||||
return anyType, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||
default:
|
||||
return anyType, fmt.Errorf("invalid argument for min (type %s)", arg)
|
||||
}
|
||||
}
|
||||
return args[0], nil
|
||||
|
||||
}
|
||||
return args[0], nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -77,8 +77,14 @@ func TestBuiltin(t *testing.T) {
|
||||
{`hasSuffix("foo,bar,baz", "baz")`, true},
|
||||
{`max(1, 2, 3)`, 3},
|
||||
{`max(1.5, 2.5, 3.5)`, 3.5},
|
||||
{`max([1, 2, 3])`, 3},
|
||||
{`max([1.5, 2.5, 3.5])`, 3.5},
|
||||
{`max([1, 2, 4, 10], 20, [29, 23, -19])`, 29},
|
||||
{`min([1, 2, 4, 10], 20, [29, 23, -19])`, -19},
|
||||
{`min(1, 2, 3)`, 1},
|
||||
{`min(1.5, 2.5, 3.5)`, 1.5},
|
||||
{`min([1, 2, 3])`, 1},
|
||||
{`min([1.5, 2.5, 3.5])`, 1.5},
|
||||
{`sum(1..9)`, 45},
|
||||
{`sum([.5, 1.5, 2.5])`, 4.5},
|
||||
{`sum([])`, 0},
|
||||
@@ -197,8 +203,10 @@ func TestBuiltin_errors(t *testing.T) {
|
||||
{`trim()`, `not enough arguments to call trim`},
|
||||
{`max()`, `not enough arguments to call max`},
|
||||
{`max(1, "2")`, `invalid argument for max (type string)`},
|
||||
{`max([1, "2"])`, `invalid argument for max (type string)`},
|
||||
{`min()`, `not enough arguments to call min`},
|
||||
{`min(1, "2")`, `invalid argument for min (type string)`},
|
||||
{`min([1, "2"])`, `invalid argument for min (type string)`},
|
||||
{`duration("error")`, `invalid duration`},
|
||||
{`date("error")`, `invalid date`},
|
||||
{`get()`, `invalid number of arguments (expected 2, got 0)`},
|
||||
|
||||
+34
-11
@@ -255,21 +255,44 @@ func String(arg any) any {
|
||||
}
|
||||
|
||||
func Max(args ...any) (any, error) {
|
||||
var max any
|
||||
for _, arg := range args {
|
||||
if max == nil || runtime.Less(max, arg) {
|
||||
max = arg
|
||||
}
|
||||
}
|
||||
return max, nil
|
||||
return minMaxFunc("max", runtime.Less, args)
|
||||
}
|
||||
|
||||
func Min(args ...any) (any, error) {
|
||||
var min any
|
||||
return minMaxFunc("min", runtime.More, args)
|
||||
}
|
||||
|
||||
func minMaxFunc(name string, fn func(any, any) bool, args []any) (any, error) {
|
||||
var val any
|
||||
for _, arg := range args {
|
||||
if min == nil || runtime.More(min, arg) {
|
||||
min = arg
|
||||
switch v := arg.(type) {
|
||||
case []float32, []float64, []uint, []uint8, []uint16, []uint32, []uint64, []int, []int8, []int16, []int32, []int64:
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Len() == 0 {
|
||||
return nil, fmt.Errorf("not enough arguments to call %s", name)
|
||||
}
|
||||
arg = rv.Index(0).Interface()
|
||||
for i := 1; i < rv.Len(); i++ {
|
||||
elem := rv.Index(i).Interface()
|
||||
if fn(arg, elem) {
|
||||
arg = elem
|
||||
}
|
||||
}
|
||||
case []any:
|
||||
var err error
|
||||
if arg, err = minMaxFunc(name, fn, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case float32, float64, uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
|
||||
default:
|
||||
if len(args) == 1 {
|
||||
return arg, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid argument for %s (type %T)", name, v)
|
||||
}
|
||||
if val == nil || fn(val, arg) {
|
||||
val = arg
|
||||
}
|
||||
}
|
||||
return min, nil
|
||||
return val, nil
|
||||
}
|
||||
|
||||
@@ -853,6 +853,22 @@ func TestExpr(t *testing.T) {
|
||||
`len({a: 1, b: 2, c: 2})`,
|
||||
3,
|
||||
},
|
||||
{
|
||||
`max([1, 2, 3])`,
|
||||
3,
|
||||
},
|
||||
{
|
||||
`max(1, 2, 3)`,
|
||||
3,
|
||||
},
|
||||
{
|
||||
`min([1, 2, 3])`,
|
||||
1,
|
||||
},
|
||||
{
|
||||
`min(1, 2, 3)`,
|
||||
1,
|
||||
},
|
||||
{
|
||||
`{foo: 0, bar: 1}`,
|
||||
map[string]any{"foo": 0, "bar": 1},
|
||||
|
||||
Vendored
+1
-1
@@ -13822,7 +13822,7 @@ min(ok ? array : false)
|
||||
min(ok ? f64 : i)
|
||||
min(ok ? half : ok)
|
||||
min(ok ? i : f32)
|
||||
min(ok ? list : score)
|
||||
min(ok ? array : score)
|
||||
min(ok ? true : div)
|
||||
min(reduce(array, #))
|
||||
min(reduce(array, 0.5))
|
||||
|
||||
Reference in New Issue
Block a user