Skip to content

Commit ae180c8

Browse files
authored
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
1 parent 6cc56e7 commit ae180c8

File tree

5 files changed

+92
-30
lines changed

5 files changed

+92
-30
lines changed

builtin/builtin.go

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -394,38 +394,53 @@ var Builtins = []*Function{
394394
Name: "max",
395395
Func: Max,
396396
Validate: func(args []reflect.Type) (reflect.Type, error) {
397-
if len(args) == 0 {
397+
switch len(args) {
398+
case 0:
398399
return anyType, fmt.Errorf("not enough arguments to call max")
399-
}
400-
for _, arg := range args {
401-
switch kind(arg) {
402-
case reflect.Interface:
400+
case 1:
401+
if kindName := kind(args[0]); kindName == reflect.Array || kindName == reflect.Slice {
403402
return anyType, nil
404-
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:
405-
default:
406-
return anyType, fmt.Errorf("invalid argument for max (type %s)", arg)
407403
}
404+
fallthrough
405+
default:
406+
for _, arg := range args {
407+
switch kind(arg) {
408+
case reflect.Interface, reflect.Array, reflect.Slice:
409+
return anyType, nil
410+
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:
411+
default:
412+
return anyType, fmt.Errorf("invalid argument for max (type %s)", arg)
413+
}
414+
}
415+
return args[0], nil
408416
}
409-
return args[0], nil
410417
},
411418
},
412419
{
413420
Name: "min",
414421
Func: Min,
415422
Validate: func(args []reflect.Type) (reflect.Type, error) {
416-
if len(args) == 0 {
423+
switch len(args) {
424+
case 0:
417425
return anyType, fmt.Errorf("not enough arguments to call min")
418-
}
419-
for _, arg := range args {
420-
switch kind(arg) {
421-
case reflect.Interface:
426+
case 1:
427+
if kindName := kind(args[0]); kindName == reflect.Array || kindName == reflect.Slice {
422428
return anyType, nil
423-
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:
424-
default:
425-
return anyType, fmt.Errorf("invalid argument for min (type %s)", arg)
426429
}
430+
fallthrough
431+
default:
432+
for _, arg := range args {
433+
switch kind(arg) {
434+
case reflect.Interface, reflect.Array, reflect.Slice:
435+
return anyType, nil
436+
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:
437+
default:
438+
return anyType, fmt.Errorf("invalid argument for min (type %s)", arg)
439+
}
440+
}
441+
return args[0], nil
442+
427443
}
428-
return args[0], nil
429444
},
430445
},
431446
{

builtin/builtin_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,14 @@ func TestBuiltin(t *testing.T) {
7777
{`hasSuffix("foo,bar,baz", "baz")`, true},
7878
{`max(1, 2, 3)`, 3},
7979
{`max(1.5, 2.5, 3.5)`, 3.5},
80+
{`max([1, 2, 3])`, 3},
81+
{`max([1.5, 2.5, 3.5])`, 3.5},
82+
{`max([1, 2, 4, 10], 20, [29, 23, -19])`, 29},
83+
{`min([1, 2, 4, 10], 20, [29, 23, -19])`, -19},
8084
{`min(1, 2, 3)`, 1},
8185
{`min(1.5, 2.5, 3.5)`, 1.5},
86+
{`min([1, 2, 3])`, 1},
87+
{`min([1.5, 2.5, 3.5])`, 1.5},
8288
{`sum(1..9)`, 45},
8389
{`sum([.5, 1.5, 2.5])`, 4.5},
8490
{`sum([])`, 0},
@@ -197,8 +203,10 @@ func TestBuiltin_errors(t *testing.T) {
197203
{`trim()`, `not enough arguments to call trim`},
198204
{`max()`, `not enough arguments to call max`},
199205
{`max(1, "2")`, `invalid argument for max (type string)`},
206+
{`max([1, "2"])`, `invalid argument for max (type string)`},
200207
{`min()`, `not enough arguments to call min`},
201208
{`min(1, "2")`, `invalid argument for min (type string)`},
209+
{`min([1, "2"])`, `invalid argument for min (type string)`},
202210
{`duration("error")`, `invalid duration`},
203211
{`date("error")`, `invalid date`},
204212
{`get()`, `invalid number of arguments (expected 2, got 0)`},

builtin/lib.go

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -255,21 +255,44 @@ func String(arg any) any {
255255
}
256256

257257
func Max(args ...any) (any, error) {
258-
var max any
259-
for _, arg := range args {
260-
if max == nil || runtime.Less(max, arg) {
261-
max = arg
262-
}
263-
}
264-
return max, nil
258+
return minMaxFunc("max", runtime.Less, args)
265259
}
266260

267261
func Min(args ...any) (any, error) {
268-
var min any
262+
return minMaxFunc("min", runtime.More, args)
263+
}
264+
265+
func minMaxFunc(name string, fn func(any, any) bool, args []any) (any, error) {
266+
var val any
269267
for _, arg := range args {
270-
if min == nil || runtime.More(min, arg) {
271-
min = arg
268+
switch v := arg.(type) {
269+
case []float32, []float64, []uint, []uint8, []uint16, []uint32, []uint64, []int, []int8, []int16, []int32, []int64:
270+
rv := reflect.ValueOf(v)
271+
if rv.Len() == 0 {
272+
return nil, fmt.Errorf("not enough arguments to call %s", name)
273+
}
274+
arg = rv.Index(0).Interface()
275+
for i := 1; i < rv.Len(); i++ {
276+
elem := rv.Index(i).Interface()
277+
if fn(arg, elem) {
278+
arg = elem
279+
}
280+
}
281+
case []any:
282+
var err error
283+
if arg, err = minMaxFunc(name, fn, v); err != nil {
284+
return nil, err
285+
}
286+
case float32, float64, uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
287+
default:
288+
if len(args) == 1 {
289+
return arg, nil
290+
}
291+
return nil, fmt.Errorf("invalid argument for %s (type %T)", name, v)
292+
}
293+
if val == nil || fn(val, arg) {
294+
val = arg
272295
}
273296
}
274-
return min, nil
297+
return val, nil
275298
}

expr_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,22 @@ func TestExpr(t *testing.T) {
853853
`len({a: 1, b: 2, c: 2})`,
854854
3,
855855
},
856+
{
857+
`max([1, 2, 3])`,
858+
3,
859+
},
860+
{
861+
`max(1, 2, 3)`,
862+
3,
863+
},
864+
{
865+
`min([1, 2, 3])`,
866+
1,
867+
},
868+
{
869+
`min(1, 2, 3)`,
870+
1,
871+
},
856872
{
857873
`{foo: 0, bar: 1}`,
858874
map[string]any{"foo": 0, "bar": 1},

testdata/examples.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13822,7 +13822,7 @@ min(ok ? array : false)
1382213822
min(ok ? f64 : i)
1382313823
min(ok ? half : ok)
1382413824
min(ok ? i : f32)
13825-
min(ok ? list : score)
13825+
min(ok ? array : score)
1382613826
min(ok ? true : div)
1382713827
min(reduce(array, #))
1382813828
min(reduce(array, 0.5))

0 commit comments

Comments
 (0)