Skip to content

Commit 5e660e7

Browse files
authored
Add flatten builtin (#684)
1 parent 5e3d5a2 commit 5e660e7

File tree

4 files changed

+58
-0
lines changed

4 files changed

+58
-0
lines changed

builtin/builtin.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,37 @@ var Builtins = []*Function{
873873
return arrayType, nil
874874
},
875875
},
876+
{
877+
Name: "flatten",
878+
Safe: func(args ...any) (any, uint, error) {
879+
var size uint
880+
if len(args) != 1 {
881+
return nil, 0, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
882+
}
883+
v := reflect.ValueOf(deref.Deref(args[0]))
884+
if v.Kind() != reflect.Array && v.Kind() != reflect.Slice {
885+
return nil, size, fmt.Errorf("cannot flatten %s", v.Kind())
886+
}
887+
ret := flatten(v)
888+
size = uint(len(ret))
889+
return ret, size, nil
890+
},
891+
Validate: func(args []reflect.Type) (reflect.Type, error) {
892+
if len(args) != 1 {
893+
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
894+
}
895+
896+
for _, arg := range args {
897+
switch kind(deref.Type(arg)) {
898+
case reflect.Interface, reflect.Slice, reflect.Array:
899+
default:
900+
return anyType, fmt.Errorf("cannot flatten %s", arg)
901+
}
902+
}
903+
904+
return arrayType, nil
905+
},
906+
},
876907
{
877908
Name: "sort",
878909
Safe: func(args ...any) (any, uint, error) {

builtin/builtin_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ func TestBuiltin(t *testing.T) {
152152
{`reduce([], 5, 0)`, 0},
153153
{`concat(ArrayOfString, ArrayOfInt)`, []any{"foo", "bar", "baz", 1, 2, 3}},
154154
{`concat(PtrArrayWithNil, [nil])`, []any{42, nil}},
155+
{`flatten([["a", "b"], [1, 2]])`, []any{"a", "b", 1, 2}},
156+
{`flatten([["a", "b"], [1, 2, [3, 4]]])`, []any{"a", "b", 1, 2, 3, 4}},
157+
{`flatten([["a", "b"], [1, 2, [3, [[[["c", "d"], "e"]]], 4]]])`, []any{"a", "b", 1, 2, 3, "c", "d", "e", 4}},
155158
}
156159

157160
for _, test := range tests {
@@ -236,6 +239,8 @@ func TestBuiltin_errors(t *testing.T) {
236239
{`now(nil)`, "invalid number of arguments (expected 0, got 1)"},
237240
{`date(nil)`, "interface {} is nil, not string (1:1)"},
238241
{`timezone(nil)`, "cannot use nil as argument (type string) to call timezone (1:10)"},
242+
{`flatten([1, 2], [3, 4])`, "invalid number of arguments (expected 1, got 2)"},
243+
{`flatten(1)`, "cannot flatten int"},
239244
}
240245
for _, test := range errorTests {
241246
t.Run(test.input, func(t *testing.T) {

builtin/lib.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,17 @@ func median(args ...any) ([]float64, error) {
359359
}
360360
return values, nil
361361
}
362+
363+
func flatten(arg reflect.Value) []any {
364+
ret := []any{}
365+
for i := 0; i < arg.Len(); i++ {
366+
v := deref.Value(arg.Index(i))
367+
if v.Kind() == reflect.Array || v.Kind() == reflect.Slice {
368+
x := flatten(v)
369+
ret = append(ret, x...)
370+
} else {
371+
ret = append(ret, v.Interface())
372+
}
373+
}
374+
return ret
375+
}

docs/language-definition.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,14 @@ Concatenates two or more arrays.
689689
concat([1, 2], [3, 4]) == [1, 2, 3, 4]
690690
```
691691

692+
### flatten(array) {#flatten}
693+
694+
Flattens given array into one-dimentional array.
695+
696+
```expr
697+
flatten([1, 2, [3, 4]]) == [1, 2, 3, 4]
698+
```
699+
692700
### join(array[, delimiter]) {#join}
693701

694702
Joins an array of strings into a single string with the given delimiter.

0 commit comments

Comments
 (0)