Functions can be used to validate syntax, calculate values, transform data, and control workflow.
Much of the content below is borrowed and adapted under the Creative Commons license from Get ODK. Note: OpenClinica does not support every function listed on the Get ODK website. Review the full list of functions that have been validated and tested in OpenClinica in the OpenClinica Validated Functions Index.
General Functions
Conditional (if statements)
| Description: | Evaluates expression, if the expression is true it returns or executes the then statement, if the expression evaluates to false it returns/executes the else statement. |
| Syntax: | if(expression, then, else) |
| Example: | if(${ae_death} = ‘yes’, ‘SAE’, if(${ae_hospital} = 'yes', 'SAE', if(...))) This nested if statement will eventually cascade through the entire SAE criteria list to see if any were answered 'Yes', and if so it will return 'SAE', if not then it will move to the next criteria and so-on. The last statement should return 'false' or similar negative value. |
Once (run something once)
| Description: | Returns the value defined by expressionif the question's value is empty. Otherwise, returns the current value of the question. This can be used to ensure that a random number is only generated once, or to store the first value entered for a question in a way that is retrievable even if the response is changed later. |
| Syntax: | once(expression) |
| Example: | once(today()) When used in a calculation, returns the current date on form load. If an expression is in a calculation, constraint, or required condition, it is evaluated when the item becomes relevant and the item is not empty. Typically used in calculations.Could also be used in relevant conditions or constraints, but in these cases its usually more clear to use as if() statement. For example: Instead of using once(), you could also if(. !='', today(), .). If it's not empty, do expression. Otherwise, leave unchanged. |
Selected (see if something was selected)
| Description: | Checks whether string2 appears in string1, where string1 is a space-separated list of values. Either or both could be item references instead of hard-coded string literals. |
| Syntax: | selected(string1, string2) |
| Example: | selected(${diagnoses}, 'cancer') Returns true if “cancer” was selected from a set of diagnoses. Otherwise, returns false. not(selected(., 'c') or selected(., 'd')) Note: Can be used as a shortcut for writing many or clauses. For example, selected('a b c d e', .) is a shortcut for ". = 'a' or . = 'b' or ' = 'c' or . = 'd' or . = 'e'." This could be useful in if() statements, relevant logic, etc. |
Count Selected (count number of selected choices)
| Description: | Returns the number of choices selected in a multiple choice question. |
| Syntax: | count-selected(select_multiple_item_name) |
| Example: | count-selected(${days_sick}) Returns the number of days the subject was sick (e.g. from a set of choices ${days_sick} such as Mon, Tue, Wed, etc.). Taking a space separated string and telling you how many of a given value exist. Can also be used to count the total number of selection choices in a space separated lists. If you concatenate strings together with a space, or join repeating data with a space, in a calculate item, this function would return the total number of items in that string. |
Functions for Repeating Data Structures
Max
| Description: | Returns the largest member of nodeset. Only works on sets of numbers. Empty values (that is, variables referencing unanswered questions) are actually empty strings, and will not be automatically converted to zero (0). Learn more about Null Values in Form Logic. | |
| Syntax: | max(nodeset) | |
| Example: | In the below example form definition, the calculation max(${child_age})would return the largest age of all the ages in the repeating item group. If you wanted to see if one of the children was, say 5 years old, you could use the join or concat function to produce a string of all the responses, then use this string as the argument for the selected function. For use with repeating item data from a different form, the max function can be used directly as part of the cross-form data pull. In this example, the largest value of the item from all repeats would be returned: max(instance('clinicaldata')/ODM/ClinicalData/SubjectData/StudyEventData[@StudyEventOID='Event OID Here']/FormData[@FormOID='Form OID Here']/ItemGroupData[@OpenClinica:ItemGroupName='Item Group Name Here']/ItemData[@OpenClinica:ItemName='Item Name Here']/@Value) | |
| type | name | label | calculation |
| begin_repeat | child_questions | Questions about child | |
| text | child_name | Child's name | |
| integer | child_age | Child's age | |
| end_repeat | |||
| calculate | age_of_oldest_child | max(${child_age}) |
Min
| Description: | Returns the smallest member of nodeset. Only works on sets of numbers. Empty values (that is, variables referencing unanswered questions) are actually empty strings, and will not be automatically converted to zero (0). Learn more about Null Values in Form Logic. |
| Syntax: | min(nodeset) |
| Example: | In the below example, the calculation min(${child_age}) would return the lowest age of all the ages in the repeating item group. |
| type | name | label | calculation |
| begin_repeat | child_questions | Questions about child | |
| text | child_name | Child's name | |
| integer | child_age | Child's age | |
| end_repeat | |||
| calculate | age_of_oldest_child | max(${child_age}) |
Sum repeats
| Description: | Returns the sum of the members of nodeset. Can be used to tally responses to a repeated select question. |
| Syntax: | sum(nodeset) |
| Example: | The below example uses this function to tally the number of meal preference. |
| type | name | label | calculation |
| begin_repeat | guest_details | Guest details | |
| text | guest_name | Guest name | |
| select_one_meal_options | meal_preference | Meal preference | |
| calculate | chkn | if(${meal_preference} = 'chicken', 1, 0 ) | |
| calculate | fsh | if(${meal_preference} = 'fish', 1, 0 ) | |
| calculate | veg | if(${meal_preference} = 'vegetarian', 1, 0 ) | |
| end_repeat | |||
| calculate | chkn_count | sum(${chkn}) | |
| calculate | fsh_count | sum(${fsh}) | |
| calculate | veg_count | sum(${veg}) |
Count repeats
| Description: | Returns the sum of the members of nodeset. Can be used to tally responses to a repeated select question. |
| Syntax: | count(nodeset) |
| Example: | The below example asks the user for the number of family members, then uses the count function to ensure follow-up questions are asked once per each family member. |
| type | name | label | repeat_count | calculation |
| note | person_list_note | Please list the names of the people in your household. | ||
| begin_repeat | person | Member of household | ||
| text | name | Name | ||
| end_repeat | ||||
| begin_repeat | person_details | Details | count(${person}) | |
| calculate | current_name | indexed-repeat(${name}, ${person}, position(..)) | ||
| date | member_bday | Birthday of ${current_name} | ||
| end_repeat |
Count non-empty repeats
| Description: | Returns the number of non-empty members of nodeset. |
| Syntax: | count-non-empty(nodeset) |
| Example: | count-non-empty(${bp_sys}) Returns the number of times systolic blood pressure was blank, where ${bp_sys} captures systolic blood pressure inside a repeating group. If a user adds a repeating group occurrence but does not put data into any of its fields, the count() function would increase by 1, but the count-non-empty() function would not increase. |
Indexed Repeat
| Description: | Returns a particular iteration of a repeating value. |
| Syntax: | indexed-repeat(item_name, repeating_group_name, iteration) item_name is the variable you want to retrieve |
| Example: | indexed-repeat(${bp_diastolic}, ${bp_rg}, ${bp_number}) When used as a calculation, returns the diastolic pressure from a specific repeat instance, where:
|
Position
| Description: | Returns an integer equal to the position of the current node within the node defined by xpath. The first entry has a position of 1, second entry 2, and so on. Most often this is used in the form position(..) to identify the current iteration within a repeating group. |
| Syntax: | position(xpath) |
| Example: | position(..) When used as a calculation inside a repeating group, will return the current repeat iteration. Could be used to assign a number for each repetition. In the example below, the first repeating group captures the names of each household member. The second repeating group asks for the birthdate for each household member entered in the first group. This shows how position() can be used with indexed-repeat(). |
| type | name | label | repeat_count | calculation |
| begin_repeat | ||||
| calculate | med_num | position(..) | ||
| text | name | Please enter the name of medication # ${med_num) | ||
| end_repeat |
String functions
Concatenate (combine values)
| Description: | Concatenates one or more arguments into a single string. If any arg is a nodeset, the values within the set are concatenated into a string. |
| Syntax: | concat(arg1, arg2, arg3...) |
| Example: | concat(${yyyy},'-',${mm},'-',${dd}) Combines year, month, and day (each captured separately) into a single item formatted YYYY-MM-DD. concat(${temp},' ⁰C') |
Convert to String (transform data type)
| Description: | Converts arg to a string. |
| Syntax: | string(arg) string references the item for which you wish to count characters |
Regular Expression (validate syntax)
| Description: | Returns True if the string is an exact and complete match for the expression. Regular expressions provide a standardized way of validating the syntax of data entered. |
| Syntax: | regex(string, expression) string is the value you want to which you want to apply the regular expression |
| Example: | regex(., '([01][0-9]|2[0-3]):[0-5][0-9]') and string-length(.) = 5 When used as a constraint, ensures the user enters time in 24-hour format (HH:MM) regex(., '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}') |
Substring (parse a string)
| Description: | Returns the substring of string beginning at the index start and extending to (but not including) index end (or to the termination of string, if end is not provided). |
| Syntax: | substr(string, start, end) string references the data item you wish to parse start is the index number of the beginning character you want to include end is the index number of the first character after startthat you do NOT want to include Members of string are zero-indexed, so the first character of the string has index value of 0, the second character an index value of 1, etc. |
| Example: | substr(${date}, 0, 4) Returns just the 4-digit year from a date field which has a format such as YYYY-MM-DD. |
Substring Before (parse a string)
| Description: | Returns the substring of string before the first occurrence of the target substring. If the target is not found, or string begins with the target substring, then this will return an empty string. |
| Syntax: | substring-before(string, target) string references the data item you wish to parse target is the stopping point (up to which the system will return the partial string) |
| Example: | substr-before(${kit_num}, ‘-’) If the value of ${kit_num} is "123-ABC-XYZ", the function would return 123. Returns the string value before the first occurrence of the target. |
Substring After (parse a string)
| Description: | Returns the substring of stringafter the first occurrence of the target substring. If the target is not found this will return an empty string. |
| Syntax: | substring-after(string, target) string references the data item you wish to parse target is the starting point, beyond which you want the system will return the remaining string |
| Example: | substr-after(${kit_num}, ‘-’) If the value of ${kit_num} is "112-ABC-XYZ", the function would return ABC-XYZ. Returns the string value after the first occurrence of the target. To return just XYZ, you could run this function two times, nested together. |
String Length (count characters in a string)
| Description: | Returns the number of characters in string. If no value is passed in, returns the number of characters in the value of the question that this function call is tied to which can be useful in a constraint expression. |
| Syntax: | string-length(string) string references the item for which you wish to count characters |
| Example: | substr(., string-length(.) - 4, string-length(.)) = ".pdf" or substr(., string-length(.) - 4, string-length(.)) = ".PDF" Ensures only PDF files are uploaded when used as a constraint on a file field. The substing function first gets the last 4 characters of the file name. Then it checks to see if the PDF file extension is all lowercase or uppercase. |
Translate
| Description: | Returns a copy of a string where every occurrence of a specified character is replaced by a new character. |
| Syntax: | translate(string, fromchars, tochars) string is the item you want to translate If fromchars is longer than tochars then every occurrence of a character in fromchars that does not have a corresponding character in tochars will be removed from the string. |
| Example: | translate(substr(., string-length(.) - 4, string-length(.)), 'pdf', 'PDF') = '.PDF' When used as a constraint on a file field, ensures the files with a PDF extension are uploaded, regardless of the case in which the file extension is written. The substing function first gets the last 4 characters of the file name. Then it checks to see if the PDF file extension is all lowercase or uppercase. translate(${input_item}, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') OpenClinica reads in cross form data from a select_multiple item as a comma separated list. However, if you want to treat this as a select_multiple value on the form you read it into, it must be treated as a space separated list. For example, the following expression would convert these values into a space separated string upon reading it into the form: translate(instance('clinicaldata')/ODM/ClinicalData/SubjectData/StudyEventData[@StudyEventOID='Event OID Here']/FormData[@FormOID='Form OID Here']/ItemGroupData[@OpenClinica:ItemGroupName='Item Group Name Here']/ItemData[@OpenClinica:ItemName='Item Name Here']/@Value, ',', ' ') |
Math functions and number handling
Negation
| Description: | |
| Syntax: | not(expression) |
| Example: |
Truncate decimal
| Description: | Truncates the fractional portion of a decimal number to return an integer. |
| Syntax: | int(number) |
| Example: | int(${age) If ${age} = 24.6, would return the value 24. |
Convert to Number
| Description: | Converts argto number value. If arg is a string of digits, returns the number value. If arg is True, returns 1. If arg is False, returns 0. If arg cannot be converted, returns NaN (not a number). |
| Syntax: | number(arg) |
| Example: | number(${year}) if ${year} = "1995", then number(${year}) would return 1995 |
Round
| Description: | Rounds a decimal number to some number of decimal places. |
| Syntax: | round(number, # places) |
| Example: | round(${pi}, 4) If ${pi} = “3.14159”, then round(${pi}, 4) would return 3.1416 |
Random Number
| Description: | Returns a random number between 0.0 (inclusive) and 1.0 (exclusive). |
| Syntax: | random() |
| Example: | once(round(random()*3+1,0)) The above expression would return and integer of either 1, 2, or 3, and could thus be used to randomly assign participants to one of three groups. The following logic could then be used to map the output of the above function to one of the three groups: if(${rndm} = 1,'A', if(${rndm} = 2,'B', 'C')) |
Date and Time
Current Date
| Description: | Returns the current date without a time component. |
| Syntax: | today() |
| Example: | The expression . <= today() could be used in a constraint to ensure the date entered is not a future date. |
Current Date and Time
| Description: | Returns the current date and time with the timezone. |
| Syntax: | now() |
| Example: | For example, this can be used to set the current date and time as default. |
Decimal Time
| Description: | Converts time to a number representing a fractional day. |
| Syntax: | decimal-time(time) |
| Example: | For example, decimal-time(12:00) noon is 0.5 and 6:00 PM is 0.75. |
Formatting Dates
| Description: | Returns date as a string formatted as defined by format. |
| Syntax: | format-date(date, format) |
| Example: | format-date(${date},‘%d-%b-%Y’) If ${date} = 2028-11-06 would return 06-Nov-2028 Month and day abbreviations are language and locale specific. If form locale can be determined, that locale will be used. Otherwise, the device locale will be used. |
| %Y | 4-digit year |
| %y | 2-digit year |
| %m | 0-padded month |
| %n | numeric month |
| %b | short text month (Jan, Feb, Mar…) |
| %d | 0-padded day of month |
| %e | day of month |
| %a | short text day (Sun, Mon, Tue…). |
Formatting Date + Time
| Description: | Returns dateTime as a string formatted as defined by format. |
| Syntax: | format-date-time(dateTime, format) |
| Example: |
The identifiers list in format-date() are available, plus the following:
| %H | 0-padded hour (24-hr time) |
| %h | hour (24-hr time) |
| %M | 0-padded minute |
| %S | 0-padded second |
| %3 | 0-padded millisecond ticks. |
Calculations
Power/Exponent
| Description: | Raises number to a power |
| Syntax: | pow(number, power) |
| Example: | pow(2,3) Returns the value 8. |
Log
| Description: | Returns the natural log of number. |
| Syntax: | log(number) |
| Example: | log(30) Returns 3.4011973816621555, the natural log of 30. |
Log Base-10
| Description: | Returns the base-10 log of number. |
| Syntax: | log10(number) |
| Example: | log10(30) Returns 1.4771212547196624, the base-10 log of 30. |
Absolute Value
| Description: | Returns the absolute value of number. |
| Syntax: | abs(number) |
| Example: | abs(-5.23) Returns 5.23. |
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article