Collections
Part of the function and operator catalog. Every example shown below is
executed as an assertion by the engine's own test suite (examples.test.ts), so a result shown
here is a result the engine actually produces for that input.
GROUPBY
GROUPBY(collection: an array of any value, fieldName: text) -> an array of an object
Groups a collection's elements by a top-level field's value, returning an array of { key, items } objects. Groups appear in the order each key first appears in the input, and items within a group appear in input order. The key must already be a string — a non-string key is a coded error, never coerced, so a number is never silently converted into a string that decides which rows land together. Keys are compared as exact UTF-16 code-unit sequences with no Unicode normalization. Addresses a top-level field by name only, not a nested path or a computed key; filter or reshape the collection first if the key you need is not top-level.
Examples:
GROUPBY(input.xs, 'k')- context:
{"xs":[{"k":"a","n":1},{"k":"b","n":2},{"k":"a","n":3}]} - result:
[{"key":"a","items":[{"k":"a","n":"1"},{"k":"a","n":"3"}]},{"key":"b","items":[{"k":"b","n":"2"}]}]
- context:
GROUPBY(input.xs, 'k')- context:
{"xs":[{"k":1}]} - fails with
EXPR_GROUP_KEY_NOT_STRING
- context:
FIND_BY
FIND_BY(collection: an array of any value, fieldName: text, value: any value) -> any value
Returns the single element whose named top-level field equals the given value, decided through the one equality predicate. Zero matches and several matches are both coded errors, the same as GET — first-wins is rejected outright, because under it the same data in a different order books differently and nothing in the trace records that a choice was made. Addresses a top-level field by name only, not a nested path or a computed key; filter or reshape the collection first if the key you need is not top-level.
Examples:
FIND_BY(input.xs, 'id', 2)- context:
{"xs":[{"id":1},{"id":2}]} - result:
{"id":"2"}
- context:
FIND_BY(input.xs, 'id', 9)- context:
{"xs":[{"id":1}]} - fails with
EXPR_NO_MATCH
- context:
FIND_BY(input.xs, 'id', 1)- context:
{"xs":[{"id":1},{"id":1}]} - fails with
EXPR_MULTIPLE_MATCHES
- context:
GET
GET(collection: an array of any value) -> any value
Returns the sole element of a one-element collection. A filter always returns an array; GET is how an author asserts that array holds exactly one thing. Zero elements is a coded no-match error, and more than one is a coded multiple-matches error — GET never picks first, because under first-wins the same data in a different order would book differently with nothing in the trace recording that a choice was made.
Examples:
GET(input.xs)- context:
{"xs":[5]} - result:
"5"
- context:
GET(input.xs)- context:
{"xs":[]} - fails with
EXPR_NO_MATCH
- context:
GET(input.xs)- context:
{"xs":[1,2]} - fails with
EXPR_MULTIPLE_MATCHES
- context:
ARGS
ARGS(first: any value, ...) -> an array of any value
Builds an array out of its arguments, in order — this is how an expression writes an array literal; there is no bracket syntax for one. There is no zero-argument form: every aggregation over an empty collection is a coded error, so an empty array literal could only ever be a way of writing an expression that fails. A filter is how an author gets a possibly-empty array.
Examples:
ARGS(1)- result:
["1"]
- result:
ARGS(1, 'a', true)- result:
["1","a",true]
- result:
SLICE
SLICE(collection: an array of any value, start: a number, end: a number) -> an array of any value
Returns the elements of a collection from start (inclusive) to end (exclusive), both counted in element positions. Both bounds must be whole numbers between zero and the collection's length inclusive — an end exactly at the length resolves (it reaches the last element), and one past it is a coded out-of-range error, the same as start. A start equal to end returns an empty array.
Examples:
SLICE(input.xs, 1, 3)- context:
{"xs":[1,2,3,4]} - result:
["2","3"]
- context:
SLICE(input.xs, 0, 3)- context:
{"xs":[1,2,3]} - result:
["1","2","3"]
- context:
SLICE(input.xs, 1, 1)- context:
{"xs":[1,2,3]} - result:
[]
- context:
SLICE(input.xs, 0, 4)- context:
{"xs":[1,2,3]} - fails with
PATH_INDEX_OUT_OF_RANGE
- context:
LENGTH
LENGTH(value: any value) -> a number
Returns a string's length in UTF-16 code units — not its grapheme-cluster count. A string holding one astral character (for example an emoji) has length two, and a precomposed and a decomposed spelling of the same grapheme have different lengths. Returns an array's element count. Any other kind of value — a number, a boolean, a date, an object or null — is a type error.
Examples:
LENGTH('ab')- result:
"2"
- result:
LENGTH('')- result:
"0"
- result:
LENGTH('😀')- result:
"2"
- result:
LENGTH(input.xs)- context:
{"xs":[1,2,3]} - result:
"3"
- context:
LENGTH(input.xs)- context:
{"xs":[]} - result:
"0"
- context:
LENGTH(1)- fails with
EXPR_TYPE_MISMATCH
- fails with
INCLUDES
INCLUDES(collection: an array of any value, value: any value) -> true/false
Answers whether a collection includes a value, deciding membership through the engine's one equality predicate — no second rule. A collection holding the string '2' does not include the number 2. String comparison is exact UTF-16 code units with no Unicode normalization, so a precomposed and a decomposed spelling of the same grapheme are different members.
Examples:
INCLUDES(input.xs, 2)- context:
{"xs":[1,2,3]} - result:
true
- context:
INCLUDES(input.xs, 4)- context:
{"xs":[1,2,3]} - result:
false
- context:
INCLUDES(input.ys, 2.0)- context:
{"ys":["2"]} - result:
false
- context:
JOIN
JOIN(collection: an array of any value, separator: text) -> text
Joins a collection's string elements with a separator. Every element must already be text (a coded error otherwise) — an engine-chosen decimal or date format must never reach output text a customer reads without anyone choosing it; convert each element explicitly first. Joining an empty collection is the empty string, since joining nothing has one obvious answer.
Examples:
JOIN(input.xs, ', ')- context:
{"xs":["a","b","c"]} - result:
"a, b, c"
- context:
JOIN(input.xs, ',')- context:
{"xs":[]} - result:
""
- context:
JOIN(input.xs, ',')- context:
{"xs":["a",1]} - fails with
EXPR_JOIN_NON_STRING
- context:
COMPACT
COMPACT(values: an array of any value) -> an array of any value
Returns an array with null elements removed, preserving the order of all other elements, including false, zero and empty strings. The element type excludes null. An empty input or an input containing only nulls returns an empty array.
Examples:
COMPACT(ARGS(input.empty, 'a', input.empty))- context:
{"empty":null} - result:
["a"]
- context:
COMPACT(ARGS(input.empty))- context:
{"empty":null} - result:
[]
- context:
COMPACT(1)- fails with
EXPR_TYPE_MISMATCH
- fails with
FLATTEN
FLATTEN(values: an array of any value) -> an array of any value
Returns an array with one level of nested arrays spliced into it, preserving order and keeping non-array elements, including null. Arrays nested more deeply remain arrays. An empty input returns an empty array.
Examples:
FLATTEN(ARGS(ARGS('a'), 'b', ARGS('c', 'd')))- result:
["a","b","c","d"]
- result:
FLATTEN(ARGS(ARGS(ARGS('a'))))- result:
[["a"]]
- result:
FLATTEN(1)- fails with
EXPR_TYPE_MISMATCH
- fails with