pkg/list: adding flattenN function as a builtin
this commit adds a new builtin to the list package for generating a
flat list from a nested list by expanding nested lists in place. only
nested lists n levels deep into the structure are expanded.
for instance
list.FlattenN([1, [[2, 3], []], [4]], 1)
results in
[1, [2, 3], [], 4]
Change-Id: I991caf51b90907783e25c780d50857c605b46a8d
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3720
Reviewed-by: Felix Ehrenpfort <felix.ehrenpfort@codecentric.de>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 56c044f..7e5d41e 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -162,6 +162,24 @@
test("list", `list.Flatten("foo")`),
`_|_(error in call to list.Flatten: cannot use value "foo" (type string) as list)`,
}, {
+ test("list", `list.FlattenN([1, [[2, 3], []], [4]], -1)`),
+ `[1,2,3,4]`,
+ }, {
+ test("list", `list.FlattenN([1, [[2, 3], []], [4]], 0)`),
+ `[1,[[2,3],[]],[4]]`,
+ }, {
+ test("list", `list.FlattenN([1, [[2, 3], []], [4]], 1)`),
+ `[1,[2,3],[],4]`,
+ }, {
+ test("list", `list.FlattenN([1, [[2, 3], []], [4]], 2)`),
+ `[1,2,3,4]`,
+ }, {
+ test("list", `list.FlattenN("foo", 1)`),
+ `_|_(error in call to list.FlattenN: cannot use value "foo" (type string) as list)`,
+ }, {
+ test("list", `list.FlattenN([], "foo")`),
+ `_|_(cannot use "foo" (type string) as number in argument 2 to list.FlattenN)`,
+ }, {
test("list", `list.Max([1, 2, 3, 4])`),
`4`,
}, {
diff --git a/cue/builtins.go b/cue/builtins.go
index e5f9044..ef2f823 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -717,6 +717,43 @@
}()
},
}, {
+ Name: "FlattenN",
+ Params: []kind{topKind, numKind},
+ Result: listKind,
+ Func: func(c *callCtxt) {
+ xs, depth := c.value(0), c.decimal(1)
+ c.ret, c.err = func() (interface{}, error) {
+ var flattenN func(Value, *internal.Decimal) ([]Value, error)
+ one := apd.New(1, 0)
+ flattenN = func(xs Value, depth *internal.Decimal) ([]Value, error) {
+ var res []Value
+ iter, err := xs.List()
+ if err != nil {
+ return nil, err
+ }
+ for iter.Next() {
+ val := iter.Value()
+ if val.Kind() == ListKind && !depth.IsZero() {
+ d := apd.New(0, 0)
+ _, err := internal.BaseContext.Sub(d, depth, one)
+ if err != nil {
+ return nil, err
+ }
+ vals, err := flattenN(val, d)
+ if err != nil {
+ return nil, err
+ }
+ res = append(res, vals...)
+ } else {
+ res = append(res, val)
+ }
+ }
+ return res, nil
+ }
+ return flattenN(xs, depth)
+ }()
+ },
+ }, {
Name: "Take",
Params: []kind{listKind, intKind},
Result: listKind,
diff --git a/pkg/list/list.go b/pkg/list/list.go
index e6b0dff..7f37933 100644
--- a/pkg/list/list.go
+++ b/pkg/list/list.go
@@ -20,6 +20,8 @@
"sort"
"cuelang.org/go/cue"
+ "cuelang.org/go/internal"
+ "github.com/cockroachdb/apd/v2"
)
// Drop reports the suffix of list x after the first n elements,
@@ -45,7 +47,7 @@
return x[n:], nil
}
-// Flatten reports a flattend sequence of the list x by expanding any elements
+// Flatten reports a flattend sequence of the list xs by expanding any elements
// that are lists.
//
// For instance:
@@ -81,6 +83,48 @@
return flatten(xs)
}
+// FlattenN reports a flattend sequence of the list xs by expanding any elements
+// depth levels deep. If depth is negative all elements are expanded.
+//
+// For instance:
+//
+// FlattenN([1, [[2, 3], []], [4]], 1)
+//
+// results in
+//
+// [1, [2, 3], [], 4]
+//
+func FlattenN(xs cue.Value, depth *internal.Decimal) ([]cue.Value, error) {
+ var flattenN func(cue.Value, *internal.Decimal) ([]cue.Value, error)
+ one := apd.New(1, 0)
+ flattenN = func(xs cue.Value, depth *internal.Decimal) ([]cue.Value, error) {
+ var res []cue.Value
+ iter, err := xs.List()
+ if err != nil {
+ return nil, err
+ }
+ for iter.Next() {
+ val := iter.Value()
+ if val.Kind() == cue.ListKind && !depth.IsZero() {
+ d := apd.New(0, 0)
+ _, err := internal.BaseContext.Sub(d, depth, one)
+ if err != nil {
+ return nil, err
+ }
+ vals, err := flattenN(val, d)
+ if err != nil {
+ return nil, err
+ }
+ res = append(res, vals...)
+ } else {
+ res = append(res, val)
+ }
+ }
+ return res, nil
+ }
+ return flattenN(xs, depth)
+}
+
// Take reports the prefix of length n of list x, or x itself if n > len(x).
//
// For instance: