1. Home
  2. Documentation
  3. Designing forms
  4. Function reference

Designing forms

Function reference

Every function Joripo's compiler knows, grouped by what it works on, with where each one runs. Built from the compiler's own table, so it lists exactly what Joripo does.

Joripo documentation · https://joripo.com/docs/designing-forms/function-reference

On this page

This list is generated from the table Joripo's form compiler itself uses, each time the page is loaded, so it cannot fall out of step with what an upload accepts. It covers every function in SurveyCTO's expression reference and the XPath core functions real SurveyCTO forms use.

How to read "where it works"

  • Everywhere, ODK Collect included. The form engine runs the function itself, with SurveyCTO's meaning.
  • Everywhere, rewritten. Joripo's compiler, which knows the form's structure, turns the function into standard XPath with the same result. ODK Collect runs the rewritten form too.
  • Joripo's form runtime only. The function has no exact equivalent in standard XPath, so Joripo's runtime implements it: the tester, web forms and Joripo Collect run it with SurveyCTO's behaviour. ODK Collect cannot open a form that uses one of these, and the upload says so.
  • No meaning outside SurveyCTO Collect for Android. Functions such as phone-call-log() read things only SurveyCTO's Android app can see. Joripo gives the same empty value SurveyCTO's own web forms give, so the column still exists.

Where SurveyCTO's documentation leaves an edge case open, the note says what Joripo chose. The operators =, !=, >, >=, <, <=, and, or, +, -, *, div and mod are standard XPath and work everywhere.

Text

FunctionWhere it worksNotes
string(field)Everywhere, ODK Collect included.Converts to text.
string-length(field)Everywhere, ODK Collect included.Length in characters.
substr(text, start, end)Everywhere, ODK Collect included.0-based start, end exclusive, as documented.
concat(a, b, ...)Everywhere, ODK Collect included.Joins any number of values.
linebreak()Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Rewritten to a string literal holding a newline.
lower(text)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Rewritten to translate() over the Latin alphabet with accents. Scripts without case (Bangla, Arabic, ...) are unaffected, as in SurveyCTO.
upper(text)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.As lower(), the other way.

Choices

FunctionWhere it worksNotes
count-selected(field)Everywhere, ODK Collect included.Items selected in a select_multiple.
selected(field, value)Everywhere, ODK Collect included.Compares choice values, never labels.
selected-at(field, n)Everywhere, ODK Collect included.0-based.
choice-label(field, value)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Rewritten to jr:choice-name(value, 'path'), in the current language. The first argument must be a single ${field}; anything else is refused, not guessed. A field that is not a select gives blank, as SurveyCTO's engine gives (reported at upload). In repeat groups: Inside a repeat, reads the current instance's value.
jr:choice-name(value, '${field}')Everywhere, ODK Collect included.SurveyCTO's older spelling of choice-label. Pointed at a field that is not a select, blank, as SurveyCTO gives (reported at upload).

Repeat groups

FunctionWhere it worksNotes
join(separator, ${field})Everywhere, ODK Collect included.Joins every instance's value. In repeat groups: Absolute path: all instances.
join-if(separator, ${field}, condition)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.join(sep, nodes[condition]); the condition is evaluated per instance. In repeat groups: Fields of the same repeat in the condition read the instance being tested; other fields read the calling context via current().
count(${repeatgroup})Everywhere, ODK Collect included.Number of instances. In repeat groups: A repeat referenced from inside itself still means all its instances.
count-if(${repeatgroup}, condition)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.count(instances[condition]). Also accepts a field inside the repeat as the first argument, which real forms use. In repeat groups: Per-instance condition, as join-if.
sum(${field})Everywhere, ODK Collect included.Sum over all instances.
sum-if(${field}, condition)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.sum(nodes[condition]). In repeat groups: Per-instance condition.
min(${field}) | min(a, b, ...)Everywhere, ODK Collect included.Minimum of a repeated field, or of several values.
min-if(${field}, condition)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.min(nodes[condition]). In repeat groups: Per-instance condition.
max(${field}) | max(a, b, ...)Everywhere, ODK Collect included.Maximum of a repeated field, or of several values.
max-if(${field}, condition)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.max(nodes[condition]). In repeat groups: Per-instance condition.
index()Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.position(<path to the enclosing repeat instance>) — correct inside a group nested in the repeat, where position(..) is not. In repeat groups: Outside any repeat it gives 1. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.
indexed-repeat(${field}, ${repeat}, n [, ${repeat2}, n2 ...])Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.ODK's indexed-repeat with each instance number wrapped so an invalid number (below 1, or not a number) uses instance 1, as documented; a number past the last instance gives blank, as SurveyCTO's data shows. In repeat groups: Field and repeat arguments are absolute; the numbers are read in the current context (so index() works from a later repeat).
rank-index(n, ${field})Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Rank 1 is the highest value; equal values are ranked in instance order (SurveyCTO: 'ordered arbitrarily', every rank distinct). Invalid index or non-numeric value gives 999. In repeat groups: Over all instances of the field's repeat.
rank-index-if(n, ${field}, condition)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.As rank-index over the instances meeting the condition; n counts the full set; an instance failing the condition gives 999.
position(..)Everywhere, ODK Collect included.ODK spelling; SurveyCTO recommends index() instead.

Lists

FunctionWhere it worksNotes
count-items(separator, list)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.Empty items count. An empty list counts 0. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.
item-at(separator, list, n)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.0-based; empty items count; out of range gives ''.
item-index(separator, list, value)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.First index, or -1.
item-present(separator, list, value)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.true when any item equals value.
de-duplicate(separator, list)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.Keeps the first occurrence of each item, in order.
rank-value(value, 'n1 n2 ...')In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.1 + the count of larger values; non-numeric value gives 999. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.

Location

FunctionWhere it worksNotes
distance-between(point1, point2)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.distance(concat(p1, ';', p2)): metres on a sphere of radius 6,378,137 m; 0 when either point is blank, as SurveyCTO's engine gives (read from its javarosa-libraries 2.81.2), so a form opens before any GPS reading. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.
area(geoshape | geotrace | ${geopoint in repeat})Everywhere, ODK Collect included.Square metres on the same sphere. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.
geo-scatter(point, range)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.Moves latitude, longitude and altitude by up to range metres.
short-geopoint(point)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.'lat lon'; blank for a blank point.

Numbers

FunctionWhere it worksNotes
number(field)Everywhere, ODK Collect included.To a number.
int(field)Everywhere, ODK Collect included.Truncates toward zero.
format-number(field)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.Groups digits in the viewer's locale; keeps the value's decimals.
round(field, digits)Everywhere, ODK Collect included.Half away from zero.
abs(n)Everywhere, ODK Collect included.
pow(base, exponent)Everywhere, ODK Collect included.
log10(n)Everywhere, ODK Collect included.
sin(n)Everywhere, ODK Collect included.Radians.
cos(n)Everywhere, ODK Collect included.Radians.
tan(n)Everywhere, ODK Collect included.Radians.
asin(n)Everywhere, ODK Collect included.Radians.
acos(n)Everywhere, ODK Collect included.Radians.
atan(n)Everywhere, ODK Collect included.Radians.
atan2(x, y)Everywhere, ODK Collect included.Radians, -pi to pi.
sqrt(n)Everywhere, ODK Collect included.
exp(n)Everywhere, ODK Collect included.
pi()Everywhere, ODK Collect included.

Dates and times

FunctionWhere it worksNotes
duration()Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Seconds the form has been open, as SurveyCTO counts them: time a saved draft sat closed is left out, and so is time the page or app spent in the background (unverified against SurveyCTO). In a plain calculate it is the total, written when the form is saved or finalized; inside once() — calculate_here timers — the moment that field is reached. Joripo's runtime keeps the time open with the draft; ODK Collect, reading the same XForm, measures wall-clock time from the start field (or a hidden meta/timeStart when the form has none) to the end field or now(). A draft saved by an older Joripo Collect resumes on that wall clock. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.
today()Everywhere, ODK Collect included.YYYY-MM-DD. In arithmetic, whole days since 1970-01-01 on the device's calendar, as in JavaRosa: today() + 1 is tomorrow's day number.
now()Everywhere, ODK Collect included.The current date-time. SurveyCTO notes that now() stored on its own keeps only the date; here it keeps the time too. Use it inside format-date-time(), as SurveyCTO advises, and both agree. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.
date(text)Everywhere, ODK Collect included.A date in arithmetic is whole days since 1970-01-01 on the device's calendar (${d} + 1 is the next day), and date() of a number is that calendar day, as in JavaRosa. A date-time counts on the same wall clock, so a date and a date-time compare as the clock says.
date-time(text)Everywhere, ODK Collect included.
decimal-date-time(text)Everywhere, ODK Collect included.Days since 1970-01-01.
decimal-time(text)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Accepts a date-time as SurveyCTO does (its example gives 0.75 for 18:00); ODK's accepts only a time, so the time part is taken. A time answer's offset is read on the device's current date, so it gives the time as entered.
format-date-time(field, format)Everywhere, ODK Collect included.Every documented code: %Y %y %m %n %b %d %e %a %H %h %M %S %3. A time question's answer is formatted too, as entered: its offset is read on the device's current date (on 1970-01-01, a zone with summer time moved it by an hour).

Enumerators

FunctionWhere it worksNotes
enumerator-name()Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.The name in the form's enumerator field, which stores "John Doe (ENID0794)": what comes before the last brackets, read as SurveyCTO's engine reads it (javarosa-libraries 2.81.2). Rewritten to Joripo's runtime function joripo-enumerator-name(field). '' when the form has no enumerator field; it deploys with a note, as SurveyCTO warns.
enumerator-id()Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.The ID in the form's enumerator field: the text inside the last brackets, or the whole value when it has none. Rewritten to joripo-enumerator-id(field). '' when the form has no enumerator field.

Phone

FunctionWhere it worksNotes
phone-call-log()Has no meaning outside SurveyCTO Collect for Android: it gives the same empty value SurveyCTO's own web forms give.'' — SurveyCTO documents it as Android-only, empty in web forms.
phone-call-duration()Has no meaning outside SurveyCTO Collect for Android: it gives the same empty value SurveyCTO's own web forms give.0, as in SurveyCTO web forms.
collect-is-phone-app()Has no meaning outside SurveyCTO Collect for Android: it gives the same empty value SurveyCTO's own web forms give.false(), as in SurveyCTO web forms.

Other

FunctionWhere it worksNotes
relevant(${field})Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.The field's relevance and every enclosing group's, and-ed together.
empty(field)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.string-length(...) = 0.
if(condition, a, b)Everywhere, ODK Collect included.
pulldata(source, column, key column, value)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Reads the attached dataset or <source>.csv. Literal column names go through the same mapping as the served CSV's header, so a column named sl no still matches.
once(expression)Everywhere, ODK Collect included.Evaluated the first time only.
once(random())Everywhere, ODK Collect included.0 <= n < 1.
coalesce(a, b, ...)Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.Any number of arguments, nested into ODK's two-argument form.
regex(field, pattern)Everywhere, ODK Collect included.JavaScript regular expressions; SurveyCTO uses Java's, which agree for ordinary patterns.
hash(a, ...)In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.SHA-256 (hex) of the arguments. SurveyCTO does not document its algorithm, so the values differ from SurveyCTO's; matching still works within Joripo. SurveyCTO's documentation leaves an edge case open; this is Joripo's choice.
uuid()Everywhere, ODK Collect included.Random UUID; use inside once().
username()In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.The signed-in Joripo user (web) or configured username.
version()Everywhere, ODK Collect included: Joripo rewrites it into standard XPath when the form is compiled.The deployed form version, inlined.
device-info()Has no meaning outside SurveyCTO Collect for Android: it gives the same empty value SurveyCTO's own web forms give.'' so the column exists; reported at upload.
plug-in-metadata(${field})In Joripo's form runtime only: Test, web forms and Joripo Collect. ODK Collect cannot open a form that uses it, and the upload says so.Metadata a field plug-in saved for that field.

XPath core functions

FunctionWhere it worksNotes
not(expr)Everywhere, ODK Collect included.
true()Everywhere, ODK Collect included.
false()Everywhere, ODK Collect included.
boolean(x)Everywhere, ODK Collect included.
contains(a, b)Everywhere, ODK Collect included.
starts-with(a, b)Everywhere, ODK Collect included.
ends-with(a, b)Everywhere, ODK Collect included.
substring-before(a, b)Everywhere, ODK Collect included.
substring-after(a, b)Everywhere, ODK Collect included.
translate(text, from, to)Everywhere, ODK Collect included.
normalize-space(text)Everywhere, ODK Collect included.
floor(n)Everywhere, ODK Collect included.
ceiling(n)Everywhere, ODK Collect included.