Skip to content

Commit 3b2df5a

Browse files
committed
Add mean() and median()
1 parent ed3d1f3 commit 3b2df5a

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

builtin/builtin.go

+130
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,136 @@ var Builtins = []*ast.Function{
367367
return args[0], nil
368368
},
369369
},
370+
{
371+
Name: "sum",
372+
Func: func(args ...any) (any, error) {
373+
if len(args) != 1 {
374+
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
375+
}
376+
v := reflect.ValueOf(args[0])
377+
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
378+
return nil, fmt.Errorf("cannot sum %s", v.Kind())
379+
}
380+
sum := int64(0)
381+
i := 0
382+
for ; i < v.Len(); i++ {
383+
it := deref(v.Index(i))
384+
if it.CanInt() {
385+
sum += it.Int()
386+
} else if it.CanFloat() {
387+
goto float
388+
} else {
389+
return nil, fmt.Errorf("cannot sum %s", it.Kind())
390+
}
391+
}
392+
return int(sum), nil
393+
float:
394+
fSum := float64(sum)
395+
for ; i < v.Len(); i++ {
396+
it := deref(v.Index(i))
397+
if it.CanInt() {
398+
fSum += float64(it.Int())
399+
} else if it.CanFloat() {
400+
fSum += it.Float()
401+
} else {
402+
return nil, fmt.Errorf("cannot sum %s", it.Kind())
403+
}
404+
}
405+
return fSum, nil
406+
},
407+
Validate: func(args []reflect.Type) (reflect.Type, error) {
408+
if len(args) != 1 {
409+
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
410+
}
411+
switch kind(args[0]) {
412+
case reflect.Interface, reflect.Slice, reflect.Array:
413+
default:
414+
return anyType, fmt.Errorf("cannot sum %s", args[0])
415+
}
416+
return anyType, nil
417+
},
418+
},
419+
{
420+
Name: "mean",
421+
Func: func(args ...any) (any, error) {
422+
if len(args) != 1 {
423+
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
424+
}
425+
v := reflect.ValueOf(args[0])
426+
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
427+
return nil, fmt.Errorf("cannot mean %s", v.Kind())
428+
}
429+
if v.Len() == 0 {
430+
return 0.0, nil
431+
}
432+
sum := float64(0)
433+
i := 0
434+
for ; i < v.Len(); i++ {
435+
it := deref(v.Index(i))
436+
if it.CanInt() {
437+
sum += float64(it.Int())
438+
} else if it.CanFloat() {
439+
sum += it.Float()
440+
} else {
441+
return nil, fmt.Errorf("cannot mean %s", it.Kind())
442+
}
443+
}
444+
return sum / float64(i), nil
445+
},
446+
Validate: func(args []reflect.Type) (reflect.Type, error) {
447+
if len(args) != 1 {
448+
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
449+
}
450+
switch kind(args[0]) {
451+
case reflect.Interface, reflect.Slice, reflect.Array:
452+
default:
453+
return anyType, fmt.Errorf("cannot avg %s", args[0])
454+
}
455+
return floatType, nil
456+
},
457+
},
458+
{
459+
Name: "median",
460+
Func: func(args ...any) (any, error) {
461+
if len(args) != 1 {
462+
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
463+
}
464+
v := reflect.ValueOf(args[0])
465+
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
466+
return nil, fmt.Errorf("cannot median %s", v.Kind())
467+
}
468+
if v.Len() == 0 {
469+
return 0.0, nil
470+
}
471+
s := make([]float64, v.Len())
472+
for i := 0; i < v.Len(); i++ {
473+
it := deref(v.Index(i))
474+
if it.CanInt() {
475+
s[i] = float64(it.Int())
476+
} else if it.CanFloat() {
477+
s[i] = it.Float()
478+
} else {
479+
return nil, fmt.Errorf("cannot median %s", it.Kind())
480+
}
481+
}
482+
sort.Float64s(s)
483+
if len(s)%2 == 0 {
484+
return (s[len(s)/2-1] + s[len(s)/2]) / 2, nil
485+
}
486+
return s[len(s)/2], nil
487+
},
488+
Validate: func(args []reflect.Type) (reflect.Type, error) {
489+
if len(args) != 1 {
490+
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
491+
}
492+
switch kind(args[0]) {
493+
case reflect.Interface, reflect.Slice, reflect.Array:
494+
default:
495+
return anyType, fmt.Errorf("cannot median %s", args[0])
496+
}
497+
return floatType, nil
498+
},
499+
},
370500
{
371501
Name: "toJSON",
372502
Func: func(args ...any) (any, error) {

builtin/builtin_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,19 @@ func TestBuiltin(t *testing.T) {
6969
{`max(1.5, 2.5, 3.5)`, 3.5},
7070
{`min(1, 2, 3)`, 1},
7171
{`min(1.5, 2.5, 3.5)`, 1.5},
72+
{`sum(1..9)`, 45},
73+
{`sum([.5, 1.5, 2.5])`, 4.5},
74+
{`sum([])`, 0},
75+
{`sum([1, 2, 3.0, 4])`, 10.0},
76+
{`mean(1..9)`, 5.0},
77+
{`mean([.5, 1.5, 2.5])`, 1.5},
78+
{`mean([])`, 0.0},
79+
{`mean([1, 2, 3.0, 4])`, 2.5},
80+
{`median(1..9)`, 5.0},
81+
{`median([.5, 1.5, 2.5])`, 1.5},
82+
{`median([])`, 0.0},
83+
{`median([1, 2, 3])`, 2.0},
84+
{`median([1, 2, 3, 4])`, 2.5},
7285
{`toJSON({foo: 1, bar: 2})`, "{\n \"bar\": 2,\n \"foo\": 1\n}"},
7386
{`fromJSON("[1, 2, 3]")`, []any{1.0, 2.0, 3.0}},
7487
{`toBase64("hello")`, "aGVsbG8="},

0 commit comments

Comments
 (0)