collections
title: Collections sidebar_position: 2
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: