Skip to content

Operators

Operators are listed from the loosest binding to the tightest. Use brackets when you are not sure.

+, -, *, /, % (remainder) and ** (power). A value that is not a number reads as 0, a numeric string such as "12" reads as a number, and dividing by zero gives 0 in a form.

1 + 2 * 3
7
(1 + 2) * 3
9
10 / 4
2.5
10 % 3
1
2 ** 3
8
5 / 0
0

A minus sign binds tighter than **, so -2 ** 2 is 4, not -4. Write -(2 ** 2) for -4.

-2 ** 2
4

~ joins two values as text. It binds looser than +, so the sum is worked out first.

{"answers": {"first": "Ada", "last": "Lovelace"}}
answers.first ~ " " ~ answers.last
"Ada Lovelace"
"n=" ~ 1 + 2
"n=3"

==, !=, <, >, <= and >=. Two numbers compare as numbers and two texts compare as text. You can chain them: a < b < c is true when all three hold, and b is read once.

{"answers": {"age": 21, "role": "manager", "a": 1, "b": 2, "c": 3}}
answers.age >= 18
true
answers.role == "manager" ? "M" : "O"
"M"
answers.a < answers.b < answers.c
true
answers.missing == null
true

Dates compare as dates once they are parsed. See Methods.

The condition builder, and you, can write the comparisons as words: is greater than, is not less than, is greater than or equal to.

{"answers": {"x": 3}}
answers.x is not less than 3
true
answers.x is greater than or equal to 3
true

&& and ||, or and and or, and ! or not. They stop as soon as the answer is known. not binds looser than a comparison, so not answers.x == 3 means not (answers.x == 3).

{"answers": {"age": 16, "consent": true, "x": 3}}
answers.age >= 18 && answers.consent == true
false
not answers.x == 3
false
answers.age >= 18 or answers.consent
true

Which values count as false: false, null, 0, "". An empty list is true. Use is_empty when you mean “has no answer”.

condition ? then : else picks one of two values. It is how a computed value changes with an answer.

answers.age >= 18 ? "adult" : "minor"
"minor"

a ?? b is a unless a is null, and then b. A cleared text box is "", not null, so ?? keeps it. Use coalesce when you want the first non-null of several.

answers.nickname ?? "Anon"
"Anon"

x in list is true when the value is in the list. It also works on a range and on text.

{"answers": {"country": "GB", "x": 3}}
answers.country in ["US", "GB"]
true
"ell" in "hello"
true

a...b is the whole numbers from a to b, with both ends. a..<b leaves out b. A range counting down is a list counting down. A range can have at most 100000 entries.

answers.x in 1...9
true
answers.x in 1..<3
false
9...7
[9,8,7]

value matches /pattern/ tests a regular expression. The match is not anchored, so use ^ and $ for the whole value. The builtin match(value, /pattern/) does the same.

{"answers": {"email": "[email protected]", "code": "ABC"}}
answers.email matches /.+@.+\..+/
true
match(answers.code, /^[A-Z]{3}$/)
true

[1, 2, 3] is a list and {a: 1, b: 2} is a record. Read a list entry with answers.tags[0].

{"answers": {"tags": ["x", "y"]}}
answers.tags[0]
"x"
len([1, 2, 3])
3