SurveyLoopr
Tips & Tricks
10 min read

Sum Values in an XLSForm Repeat Group

Mehrab Ali
Mehrab AliPublished November 9, 2026

To add up a number collected in a repeat group, put sum(${field}) in a calculate row after the end_repeat row, where field is the question inside the repeat. Outside the repeat, ${field} stands for that answer in every instance, and sum() adds them; count(${repeat_name}) gives the number of instances. Every example below compiles with pyxform and ODK Validate, and works in ODK, KoboToolbox and SurveyCTO.

Download example form (.xlsx)All six examples in one XLSForm, one group each. Tested with pyxform and ODK Validate; free to reuse under CC BY 4.0.Download

How do I sum a field from a repeat group?

A trader lists each sale in a repeat. The total goes in a calculate below end_repeat, and a note shows it:

typenamelabelcalculation
begin_repeatsaleSale
textproductWhich product was sold?
decimalsale_amountWhat was the total amount for ${product}?
end_repeatsale
calculatetotal_salessum(${sale_amount})
calculaten_salescount(${sale})
noteshow_totalTotal of ${n_sales} sales: ${total_sales}
surveychoicessettings

Two things decide whether this works:

  • Position. The calculate sits outside the repeat. Inside the repeat, ${sale_amount} means "this instance's amount", so sum() there adds up one value.
  • One argument. sum() takes a single nodeset. sum(${a}, ${b}) is rejected; to add two repeated fields, write sum(${a}) + sum(${b}).

count() takes the repeat's name (${sale}), not a question inside it. For reading one specific instance rather than the total, use indexed-repeat().

How do I count the instances that match a condition?

To count children under five in a household roster, mark each member with 1 or 0 inside the repeat, then add up the marks outside it:

typenamelabelcalculation
begin_repeatmemberHousehold member
textmember_nameWhat is the member's name?
integermember_ageHow old is ${member_name}?
calculateis_under5if(${member_age} < 5, 1, 0)
end_repeatmember
calculatehh_sizecount(${member})
calculaten_under5sum(${is_under5})
noteshow_under5${n_under5} of ${hh_size} members are under five.
surveychoicessettings

The same pattern counts anything per instance: women of reproductive age, members who attend school, plots that are irrigated. Write the test once, inside the repeat, with an if() statement, and sum() does the counting.

How do I sum only some instances (a conditional sum)?

A conditional sum works the same way: a calculate inside the repeat holds the amount when the condition is met and 0 when it is not. Here, only members who worked for pay in the last month add to household earnings:

typenamelabelcalculationrelevant
begin_repeatearnerHousehold member
textearner_nameWhat is the member's name?
select_one yes_noworkedDid ${earner_name} work for pay in the last 30 days?
integerearningsHow much did ${earner_name} earn in the last 30 days?${worked} = '1'
calculateearnings_countedif(${worked} = '1', coalesce(${earnings}, 0), 0)
end_repeatearner
calculatehh_earningssum(${earnings_counted})
noteshow_earningsHousehold earnings in the last 30 days: ${hh_earnings}
surveychoicessettings
list_namenamelabel
yes_no1Yes
yes_no0No
surveychoicessettings

For a select_multiple question inside the repeat, use selected(${field}, 'name') as the condition, or count-selected(${field}) to add up how many options were ticked in each instance.

Warning:

One empty answer blanks the whole total. A skipped or unanswered number is not 0, and sum() over it returns no value at all. Sum a calculate that holds coalesce(${earnings}, 0) (as above) whenever the number has a relevant or is not required.

How do I calculate an average from a repeat?

Divide the sum by the count. XLSForm divides with div, not /:

typenamelabelcalculation
begin_repeatchildChild
textchild_nameWhat is the child's name?
decimalmuac_cmWhat is ${child_name}'s MUAC in cm?
end_repeatchild
calculaten_childrencount(${child})
calculatemean_muacif(${n_children} > 0, round(sum(${muac_cm}) div ${n_children}, 1), '')
noteshow_meanAverage MUAC for ${n_children} children: ${mean_muac} cm
surveychoicessettings

The if() stops a division by zero when no child was added. round(..., 1) keeps one decimal place. More arithmetic patterns are in the XLSForm calculation examples.

How do I make the repeat entries add up to 100%?

A farmer lists each crop and the share of land it takes. Sum the shares after the repeat and stop the form until they add up to 100, with a constraint on an acknowledge question:

typenamelabelcalculationconstraintconstraint_message
begin_repeatcropCrop
textcrop_nameWhich crop is grown?
integercrop_shareWhat percentage of the land is under ${crop_name}?. >= 1 and . <= 100Enter a share from 1 to 100.
end_repeatcrop
calculatetotal_sharesum(${crop_share})
acknowledgecheck_shareThe crop shares add up to ${total_share}%. Confirm to continue.${total_share} = 100The shares add up to ${total_share}%. Go back and correct them so they total 100%.
surveychoicessettings

The enumerator cannot finalize the form while the total is wrong, and the message shows the actual total, so they know how far off it is. The constraint examples page has more cross-question checks.

How do I sum a nested repeat?

With a repeat inside a repeat, where the calculate sits decides what it adds. Inside the outer repeat, sum() adds only that instance's inner entries; after both repeats, it adds everything:

typenamelabelcalculation
begin_repeathh_memberHousehold member
texthm_nameWhat is the member's name?
begin_repeatincome_sourceIncome source
textsource_nameWhat is the income source?
decimalsource_amountHow much did ${hm_name} earn from ${source_name} last month?
end_repeatincome_source
calculatemember_incomesum(${source_amount})
noteshow_member_income${hm_name} earned ${member_income} last month.
end_repeathh_member
calculatehousehold_incomesum(${source_amount})
noteshow_household_incomeHousehold income last month: ${household_income}
surveychoicessettings

pyxform writes ${source_amount} as a relative path in member_income (this member's sources) and an absolute path in household_income (every member's sources). You write the same reference both times.

Common repeat sum errors and how to fix them

These are the messages ODK Validate returns, which is what ODK Central, KoboToolbox or the XLSForm converter shows when it rejects the form:

You wrote (outside the repeat)Write insteadError you will see
${sale_amount} * 2sum(${sale_amount}) * 2This field is repeated: ... You may need to use the indexed-repeat() function to specify which value you want.
SUM(${sale_amount})sum(${sale_amount})cannot handle function 'SUM'
sum(${a}, ${b})sum(${a}) + sum(${b})cannot handle function 'sum'. Requires 1 arguments but 2 provided.
sum(${sale_amount} > 5)a 1/0 calculate inside the repeat, then sum() of itXPath evaluation: type mismatch not a nodeset
sum(${sale_amt}) (misspelt name)the exact name from the survey sheetCould not find the name 'sale_amt'.
sum(${amount}) where amount can be skippedsum() of a coalesce(${amount}, 0) calculateNo error, but the total is blank
sum(${amount}) inside the repeatmove the calculate below end_repeatNo error, but each instance shows only its own value

The last two rows compile and look fine in the builder, and fail only once real data arrives. For other messages, see common XLSForm errors or paste the form into the free XLSForm validator.

Does this work the same in ODK, KoboToolbox and SurveyCTO?

Yes. All three use the ODK XPath functions sum() and count(), and the same rule that a repeated field outside its repeat means every instance. What differs is where you type it:

PlatformWhere the sum goes
ODK (XLSForm)calculation column of a calculate row after end_repeat
KoboToolbox FormbuilderA Calculate question placed after the repeat group, with sum(${field}) in its calculation box
KoboToolbox (XLSForm upload)Same as ODK
SurveyCTOcalculation column of a calculate field after the repeat (skip logic goes in a column named relevance)

In the exported data, the totals land on the main sheet, one row per submission, while the repeat's answers go to a separate sheet. That is the main reason to compute them in the form: the analyst gets the household total without merging sheets.

Why totals belong in the form

A household total calculated during the interview can be checked on the spot. A note that says "Household income last month: 0" when three members said they worked tells the enumerator something was skipped, while the respondent is still there to answer. The same total built in Stata a month later only tells you the record is wrong.

Tip:

You can describe the total in plain language instead: "count the members under five and add up the earnings of members who worked". LooprAI writes the calculates inside and after the repeat, then compiles the form with pyxform before anything is saved, so a calculate in the wrong place never reaches your form.

Build my first form free

Next: XLSForm repeat groups →

Frequently Asked Questions

How do I sum a field from a repeat group in XLSForm?

Add a calculate row after the end_repeat row and write sum(${field}) in its calculation column, where field is the number question inside the repeat. Outside the repeat, ${field} refers to every instance, and sum() adds them into one total.

How do I count the number of entries in a repeat?

Use count() with the repeat's own name, for example count(${member}) in a calculate after the repeat. It returns how many instances were added. To count only some instances, add a 1 or 0 calculate inside the repeat and sum it.

Why is my repeat group sum blank?

Usually because one instance has an empty value. A skipped or unanswered number is not zero, and sum() over an empty value returns nothing. Add a calculate inside the repeat with coalesce(${amount}, 0) and sum that calculate instead of the question.

Why does my form say 'This field is repeated' when I use a repeat answer?

You referenced a question from inside a repeat in a calculation outside it without a function that takes many values. Wrap it in sum() for a total, use count() on the repeat for the number of instances, or indexed-repeat() to read one instance.

How do I make repeat entries add up to 100 percent?

Sum the percentages in a calculate after the repeat, then add an acknowledge question with the constraint ${total_share} = 100 and a constraint message that shows the total. The form cannot be finalized until the shares add up to exactly 100.

Does sum() work the same in KoboToolbox and SurveyCTO?

Yes. ODK, KoboToolbox and SurveyCTO all support sum() and count() on repeated fields. In Kobo's Formbuilder you add a Calculate question after the repeat group; in XLSForm and SurveyCTO the expression goes in the calculation column of a calculate row.

No Credit Card Required

Build the form first. Add hosting when you need it.

Start free with LooprAI, then deploy a managed ODK Central server or run DataSnap checks when your project is ready.

Automation handles the mechanical work.

Researchers decide what the evidence means.