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:
Ganesan Karuppasamy
2024-02-24 21:12:51 +01:00
committed by GitHub
parent 14d3c281b2
commit 2f8f25a68d
5 changed files with 92 additions and 30 deletions
+33 -18
View File
@@ -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
},
},
{
+8
View File
@@ -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
View File
@@ -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
}
+16
View File
@@ -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},
+1 -1
View File
@@ -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))