Skip to content
Talk to our solutions team

`map` and `array` — collections

map and array are how a rule works with collections. They exist because the rule language cannot index or iterate one: there is no [0], no for, no comprehension. Every access goes through a function call, and every one of these returns a default rather than raising.

That constraint shapes what rules look like. A rule does not walk a table; it asks a question about it — find the row whose type is “principal”, how many rows are there, does this list contain the code. Where you genuinely need per-row logic, the answer is usually a table read (Rows) or a rule that fires per row, not a loop.

Returns a fresh empty map.

Builds a map from alternating key/value pairs.

ParameterTypeMeaning
kvany, variadickey1, value1, key2, value2, …
rule RecordDecision "attach the decision inputs to the audit trail" {
when
out.Has("loanAmount") && out.Has("income")
then
audit.LogWithData("RecordDecision", "affordability inputs",
map.Of("amount", out.Get("loanAmount"),
"income", out.Get("income"),
"ratio", math.Round(to.Num(out.Get("loanAmount")) / to.Num(out.Get("income")), 2)));
}

An odd number of arguments is a mistake the engine cannot catch for you — count them.

FunctionReturns
map.GetStr(m, key, defaultVal)string
map.GetInt(m, key, defaultVal)int64
map.GetFloat(m, key, defaultVal)float64
map.GetBool(m, key, defaultVal)bool
map.GetMap(m, key)map, or nil
map.GetList(m, key)list, or nil
ParameterTypeMeaning
mmapThe map to read
keystringThe key
defaultValmatching typeReturned when the key is absent
rule ReadRowAmount "the amount column, defaulting to zero" {
when
array.Len(to.List(in.Rows("invoice.lines"))) > 0
then
out.Set("firstLineAmount",
map.GetFloat(array.First(to.List(in.Rows("invoice.lines"))), "amount", 0.0));
}

Note the decimal point on 0.0 — the same type-dispatch rule as elsewhere.

Writes into a map in place.

ParameterTypeMeaning
mmapThe map to modify
keystringThe key
valueanyThe value

For results, prefer out.Set — it records the writing rule and carries attributes. Use map.Set for a map you are assembling as a value.

Removes a key, in place.

ParameterTypeMeaning
mmapThe map to modify
keystringThe key to remove

Reports whether the key is present.

ParameterTypeMeaning
mmapThe map to test
keystringThe key

Every key in the map.

How many entries.

Whether the map has no entries. Distinct from nil, and true for both.

A fresh empty list.

How many elements.

ParameterTypeMeaning
arrlistThe list

The one function you can call on a collection the rule language will not let you index. A read that returns a list — Records(), Headings(), Items() — can be counted with this even when nothing else can touch it.

rule TooManyBorrowers "more than four borrowers needs manual review" {
when
array.Len(to.List(in.Rows("application.borrowers"))) > 4
then
findings.Warn("TooManyBorrowers", "underwriting",
"More borrowers than the automated path handles");
}

The element at index, zero-based.

ParameterTypeMeaning
arrlistThe list
indexint64Zero-based position

array.GetStr(arr, index, defaultVal) → string · array.GetInt(...) → int64 · array.GetFloat(...) → float64

Section titled “array.GetStr(arr, index, defaultVal) → string · array.GetInt(...) → int64 · array.GetFloat(...) → float64”

Typed access with a default, for an index that may not exist.

ParameterTypeMeaning
arrlistThe list
indexint64Zero-based position
defaultValmatching typeReturned when the index is out of range

array.First(arr) → any · array.Last(arr) → any

Section titled “array.First(arr) → any · array.Last(arr) → any”

The first or last element.

ParameterTypeMeaning
arrlistThe list

Last is the one worth remembering — reaching the final element without knowing the length is otherwise a two-step.

array.Contains(arr, value) → bool · array.ContainsStr(arr, value) → bool

Section titled “array.Contains(arr, value) → bool · array.ContainsStr(arr, value) → bool”

Membership. ContainsStr takes a list of strings specifically, which is what most reads produce.

ParameterTypeMeaning
arrlistThe list to search
valueany / stringWhat to look for
rule KnownFormType "the classified type is one we handle" {
when
!array.ContainsStr(vocab.Items("supported_forms"), out.Str("docType", ""))
then
findings.Error("KnownFormType", "classification",
strings.Sprintf("Unsupported form type: %s", out.Str("docType", "")));
}

The position of the first occurrence, or -1 when absent.

ParameterTypeMeaning
arrlistThe list to search
valueanyWhat to look for

array.Append(arr, value) → list · array.Prepend(arr, value) → list

Section titled “array.Append(arr, value) → list · array.Prepend(arr, value) → list”

Return a new list with the element added at the end or the start.

ParameterTypeMeaning
arrlistThe starting list
valueanyWhat to add

They return a new list rather than mutating, so the result has to be captured — out.Set it, or pass it on.

A new list in reverse order.

A sub-range, start inclusive and end exclusive.

ParameterTypeMeaning
arrlistThe list
startintFirst index, inclusive
endintLast index, exclusive

The two functions that make table work possible without iteration.

Returns the first row whose key column equals value, or an empty map.

ParameterTypeMeaning
rowslist of mapThe rows to search
keystringThe column name
valuestringThe value to match
rule PrincipalPaymentPresent "the payment table has a principal row" {
when
map.Has(array.FindRowBy(to.Rows(bbox.Doc().Table().Records()), "type", "Principal"), "amount")
then
out.Set("principal",
map.GetFloat(
array.FindRowBy(to.Rows(bbox.Doc().Table().Records()), "type", "Principal"),
"amount", 0.0));
}

array.FindRowByRegex(rows, key, pattern) → map

Section titled “array.FindRowByRegex(rows, key, pattern) → map”

The same, matching the column against a regular expression.

ParameterTypeMeaning
rowslist of mapThe rows to search
keystringThe column name
patternstringThe regular expression to match

The form to use when the label varies between documents — "Principal", "Principal & Interest", "Principal and Interest" — which on extracted tables it usually does.

rule PrincipalRowFlexible "match however this document words the principal row" {
when
map.Has(
array.FindRowByRegex(to.Rows(bbox.Doc().Table().Records()), "type", "(?i)^principal"),
"amount")
then
out.Set("principal",
map.GetFloat(
array.FindRowByRegex(to.Rows(bbox.Doc().Table().Records()), "type", "(?i)^principal"),
"amount", 0.0));
}

Deliberate. A rule states a condition and an action; a loop inside one is a program, and a program inside a rule is where rule engines stop being reviewable.

Where you find yourself wanting one:

You want toDo this instead
Act on each rowA Rows read, then hand Records() to out.Set
Find one rowarray.FindRowBy / FindRowByRegex
Count rows meeting a conditionExtract the column, then count
Aggregate a columnto.NumList on the column, which skips non-numbers
Genuinely iterate with logicA script, called before the rules run