# Common Recipes {#common-recipes}


Practical, copy-and-adapt examples for real surveys. Every expression uses
realistic variable names — replace them with your survey's question names.

For the exact behaviour of each function, see the
[Expression Reference](/user-manual/expression-reference.md) and
[Date and Time Reference](/user-manual/date-time-reference.md).

---


## Age {#age}


**Question:** `dob` (date) · **Calculated question:** `age`

```text
age(${dob}, ${interview_date})
```

where `interview_date` is an ordinary **date question you create** in the
survey ("Interview date"). MQUAD does not invent an interview date — the
calculation needs two explicitly supplied dates.

Alternatively, use today's date:

```text
age(${dob}, today())
```

**The difference:** `${interview_date}` is fixed once answered; `today()` is
re-evaluated each time the calculation runs, so the age creeps up to the next
birthday on the day itself.


## Child age in months {#child-age-in-months}


```text
age-months(${dob}, ${visit_date})
```

For a child under two, months are more meaningful than years.


## Pregnancy week {#pregnancy-week}


**Questions:** `lmp` (date) · `visit_date` (date)

```text
gestational-weeks(${lmp}, ${visit_date})
```


## Pregnancy week + remaining days {#pregnancy-week--remaining-days}


Two calculated fields, usually shown together as "16 weeks + 4 days":

```text
gestational-weeks(${lmp}, ${visit_date})
```

```text
gestational-days(${lmp}, ${visit_date})
```

`gestational-days` returns the 0–6 remainder, and
`gestational-total-days(${lmp}, ${visit_date})` returns the raw total.


## BMI {#bmi}


**Questions:** `weight_kg` (decimal) · `height_cm` (decimal)

```text
bmi(${weight_kg}, ${height_cm})
```

**Height is in centimetres.** `bmi(70, 175)` ≈ `22.86`. Do not pass metres.


## Days between visits {#days-between-visits}


```text
date-diff('days', ${visit_1}, ${visit_2})
```

This is **signed**: reversing the arguments negates the result. Prefer it over
the legacy `days-between`, whose argument order differs by platform.


## Months since enrollment {#months-since-enrollment}


```text
date-diff('months', ${enrollment_date}, ${visit_date})
```

Completed months — a visit exactly 30 days after enrollment is still `0`
months.


## Follow-up date {#follow-up-date}


```text
date-add(${visit_date}, 14, 'days')
```

Adds 14 calendar days. Month-end clamping applies to `'months'`/`'years'`
units: `date-add('2026-01-31', 1, 'months')` → `2026-02-28`.


## Estimated due date {#estimated-due-date}


A generic date calculation (presented as a calculation, not medical advice):

```text
date-add(${lmp}, 280, 'days')
```


## Interview duration {#interview-duration}


Using the legacy `duration` (end first!):

```text
duration(${end_time}, ${start_time}, 'minutes')
```

or with the newer `time-diff`:

```text
time-diff('minutes', ${start_time}, ${end_time})
```

`time-diff` treats both plain times as clock values with no midnight rollover
(`23:00 → 01:00` is `-22` hours). For durations that cross midnight, use
datetime questions instead.


## Show "Other, specify" {#show-other-specify}


**Skip logic** on the "Other, specify" question, shown when **Other** is
selected in a select-multiple:

```text
selected(${symptoms}, 'other')
```

For a select-one the simpler equality works:

```text
${symptoms} = 'other'
```


## Minimum number of selections {#minimum-number-of-selections}


**Validation** on a select-multiple question:

```text
count-selected(${symptoms}) >= 2
```


## Age-based section {#age-based-section}


**Skip logic** on the first adult question (age is a calculated question):

```text
${age} >= 18
```


## Pregnancy section {#pregnancy-section}


**Skip logic** on the first pregnancy question:

```text
${pregnant} = 'yes'
```


## Child eligibility {#child-eligibility}


Show child-specific questions when the respondent is pregnant **and** under a
certain age (combines a calculated value and an answer):

```text
${pregnant} = 'yes' and ${age} < 18
```


## Copy household identifier {#copy-household-identifier}


**Default value** on a text question:

```text
#household_num
```


## Generate compound identifier {#generate-compound-identifier}


**Calculation** on a text question (functions available):

```text
concat(${state}, '-', ${district}, '-', ${household_num})
```

produces e.g. `MH-PUNE-0012`. (In a *default value* the same composition uses
`#state-#district-#household_num` — no functions there.)


## Conditional calculated value {#conditional-calculated-value}


```text
if(${age} >= 18, 'adult', 'child')
```


## Date validation {#date-validation}


End date cannot be earlier than start date — **validation** on `end_date`:

```text
${end_date} >= ${start_date}
```


## Numeric range validation {#numeric-range-validation}


Weight must be between realistic bounds — **validation** on `weight_kg`:

```text
. >= 2 and . <= 200
```

The dot `.` is the current question's value.


## Required only under a condition {#required-only-under-a-condition}


MQUAD's `required` flag is unconditional. To make a field required only when a
condition holds, use `if()` as a **validation**:

```text
if(${has_children} = 'yes', . != '', .)
```

---


# Advanced examples {#advanced-examples}



## Multiple conditions {#multiple-conditions}


Show a question when the respondent is female, aged 15–49, and married:

```text
${sex} = 'female' and ${age} >= 15 and ${age} <= 49 and ${marital_status} = 'married'
```

Read: "female AND age 15 to 49 AND married".


## Multiple-choice logic {#multiple-choice-logic}


Show treatment-details questions when any relevant treatment was selected:

```text
selected(${treatments}, 'antibiotics') or selected(${treatments}, 'hospital') or count-selected(${treatments}) > 0
```

Since any selection means at least one, the simpler form works too:

```text
count-selected(${treatments}) >= 1
```


## Chained calculated values {#chained-calculated-values}


A dependency chain — DOB → Age → Age group → section visibility:

1. `dob` (date question, answered by the respondent)
2. `age` (calculated): `age(${dob}, ${interview_date})`
3. `age_group` (calculated): `if(${age} < 5, 'under5', if(${age} < 18, 'child', 'adult'))`
4. Section skip logic: `${age_group} = 'adult'`

When `dob` changes, MQUAD's dependency graph re-evaluates `age`, then
`age_group`, then the section visibility — in that order.


## BMI chain {#bmi-chain}


1. `weight_kg` and `height_cm` (answered)
2. `bmi` (calculated): `bmi(${weight_kg}, ${height_cm})`
3. `bmi_category` (calculated):

```text
if(${bmi} < 18.5, 'underweight', if(${bmi} < 25, 'normal', if(${bmi} < 30, 'overweight', 'obese')))
```

`if()` nests: the second `if` runs only when the first condition is false.


## Pregnancy chain {#pregnancy-chain}


1. `lmp` (answered)
2. `gest_age_weeks` (calculated): `gestational-weeks(${lmp}, ${visit_date})`
3. `trimester` (calculated):

```text
if(${gest_age_weeks} < 13, 'first', if(${gest_age_weeks} < 28, 'second', 'third'))
```

4. Follow-up questions with skip logic like `${trimester} = 'third'`

There is no dedicated trimester function — conditional expressions derive it,
as shown.


## Conditional calculated value with numbers {#conditional-calculated-value-with-numbers}


```text
if(${member_status} = 'premium', ${total} * 0.2, 0)
```

20% discount for premium members, else 0.
