Operators
Operators are listed from the loosest binding to the tightest. Use brackets when you are not sure.
Arithmetic
Section titled “Arithmetic”+, -, *, /, % (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 * 37(1 + 2) * 3910 / 42.510 % 312 ** 385 / 00A minus sign binds tighter than **, so -2 ** 2 is 4, not -4. Write -(2 ** 2) for -4.
-2 ** 24Joining text
Section titled “Joining text”~ 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"Comparison
Section titled “Comparison”==, !=, <, >, <= 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 >= 18trueanswers.role == "manager" ? "M" : "O""M"answers.a < answers.b < answers.ctrueanswers.missing == nulltrueDates compare as dates once they are parsed. See Methods.
Words for comparison
Section titled “Words for comparison”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 3trueanswers.x is greater than or equal to 3true&& 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 == truefalsenot answers.x == 3falseanswers.age >= 18 or answers.consenttrueWhich values count as false: false, null, 0, "". An empty list is true. Use is_empty when you mean “has no answer”.
The ternary
Section titled “The ternary”condition ? then : else picks one of two values. It is how a computed value changes with an answer.
answers.age >= 18 ? "adult" : "minor""minor"Fallback with ??
Section titled “Fallback with ??”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"Membership with in
Section titled “Membership with in”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"trueRanges
Section titled “Ranges”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...9trueanswers.x in 1..<3false9...7[9,8,7]Matching text
Section titled “Matching text”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 matches /.+@.+\..+/truematch(answers.code, /^[A-Z]{3}$/)trueLists and records
Section titled “Lists and records”[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