Filtering values in a repeat, in another repeat. indexed-repeat issue and Enketo vs Collect/webforms difference in behaviour

Continuing from my troubles with tribbles dynamic repeat labels, now I'm trying to filter the contents of a repeat... dynamically

1. What is the issue? Please be detailed.
I have a form where the enumerator selects one item, then via a repeat reports a variable number of findings against it. The findings (child) are identified from a select one, and each is assigned to a parent category via a field against the choice/entity list. Each response against the finding has an associated severity value that is looked up.

I would like to summarise the findings in a parent repeat at the end of the form, filtering the child findings according to which parent they belong to, as well as returning the maximum severity for that parent.

Outside of a repeat I have working logic in Collect/Enketo/Webforms, however you have to select the parent / hard code the parent, and I wish to have a dynamically sized array for varying parent lists.

I can count the parent items in the choice/entity list and create a repeat with that many elements and then iterate through the list using position() [1]. The repeat count is correct and each repeat element correctly shows the parent value that is looked up in turn.

However, the join/max/count calculations that work outside a repeat don't work in the repeat, so I tried to write them differently and filter the repeat values with indexed-repeat to count/join/find maximum. This works in Enketo[2], but in Web Forms every repeat element returns the outcome for the first item in the choice list.

eg a count (join and max are much the same) in the parent repeat;

count(${repeat5}[finding_parent = indexed-repeat(${calculate39}, ${repeat31}, position(..))])

  • repeat5 is the child repeat
    • finding_parent is the lookup of the selected child category's parent
  • calculate39 is the lookup for the parent name value in the parent repeat, this works fine in Collect/Enketo/Webforms
    • instance('parent')/root/item[name != ''][position()=${calculate38}]/name
      • calculate38 returns position(..) for the parent summary repeat
  • repeat31 is the parent repeat where I am attempting to build the summary

2. What steps can we take to reproduce this issue?
Test the attached form in Enketo/Web forms/Collect

3. What have you tried to fix the issue?
I have searched the forum & docs but it's mostly about a 1:1 relationship of using a value from repeat-a element x in repeat-b element x, I can't find anything that pertains to actually filtering the content of a repeat to combine multiple values from a repeat into a different repeat when writing expressions.

I'm especially stymied as it does evaluate properly in Enketo, and Collect & Web Forms evaulates without error but always as if the position value = 1 regardless of parent repeat position #, so not usable.

I think I've isolated it to the indexed-repeat filter expression eg;

join(', ' , ${repeat5}[finding_parent = indexed-repeat(${calculate39}, ${repeat31}, position(..))]/group6/select8)

  • Replacing position(..) with a calculate field that returns position(..) breaks the outcome in Enketo and web forms, Collect still evaluates all repeat elements for the first item in the choice list in the filter
  • wrapping position(..) in int() has no effect
  • Hard coding the indexed-repeat expression forces it to work for not the first choice item in Collect/Web Forms, which is why I think the indexed-repeat part is the issue. eg;
    • join(', ' , ${repeat5}[finding_parent = 'air']/group6/select8)

  • Likewise, hard coding an integer in place of position(..) forces it to work for not the first choice item in Collect/Web Forms
    • join(', ' , ${repeat5}[finding_parent = indexed-repeat(${calculate39}, ${repeat31}, 2)]/group6/select8)

  • Flipping the order of field and expression in the filter has no effect
    • join(', ' , ${repeat5}[indexed-repeat(${calculate39}, ${repeat31}, position(..)) = finding_parent]/group6/select8)

  • Replacing the indexed-repeat with the field that contains the parent name value doesn't evalulate at all, even though that field should be correct in each repeat element (and does show in a note correctly everywhere).
    • join(', ' , ${repeat5}[finding_parent = ${calculate39}]/group6/select8)

  • Replacing the indexed-repeat with a separate field (calculate46) that contains the indexed-repeat expression doesn't evalulate at all, even though the field with the indexed-repeat does evaulate correctly and shows in a note correctly for each repeat element.
    • join(', ' , ${repeat5}[finding_parent = ${calculate46}]/group6/select8)

Hoping @LN will instantly spot a rookie error in my calc logic and it's not a deeper issue :rofl:

4. Upload any forms or screenshots you can share publicly below.

Example form, first repeat is the child, then there's a manual select to filter values from the child repeat (works), then there's a parent repeat with the issues.

filtered repeat values for rollup and summary.xlsx (596.7 KB)


  1. Chapeau! This is more efficient than my earlier approach of joining a list of the parent name values and then using selected-at and converting from 0 indexed to 1 indexed to pull each one in turn ↩︎

  2. Mostly! You have to add a blank child repeat in order for the parent repeat to include the penultimate element in the outcome, so it doesn't properly work in Enketo either ↩︎

1 Like

Hi @ahblake!

You can try using current() in calculate40.

From this:
count(${repeat5}[finding_parent = indexed-repeat(${calculate39}, ${repeat31}, position(..))])

To this:
count(${repeat5}[finding_parent = current()/${calculate39}])

I've added this calculation to your example:
filtered repeat values for rollup and summary - FIXED.xlsx (285.7 KB)

This is how it works:

1 Like

Ah thank you! SO much simpler. No idea why a standalone indexed-repeat evaluates correctly but when used in the filter it doesn't :person_shrugging:

I had seen the current() operator, but in the context where it wrapped position(..) like this example in the docs which also a kind of repeat filter (filter to the previous repeat element only), not sure why I discarded that approach.

The problem now is that Enketo won't load the modified version (which works in Collect & Web forms), so no edits could be made in Central (until Web forms has further features added).

FormLogicError: Could not evaluate: count( /model/instance[1]/data/repeat5 [finding_parent = /model/instance[1]/data/repeat31/calculate40/ ../calculate39 ]), message: Failed to execute 'createExpression' on 'XPathEvaluator': The string './data/repeat31/calculate40/' is not a valid XPath expression.

removing the count expression cascades the error to the next expression

FormLogicError: Could not evaluate: join(', ' , /model/instance[1]/data/repeat5 [finding_parent = /model/instance[1]/data/repeat31/calculate41/ ../calculate39 ]/calc_finding_ID), message: Failed to execute 'createExpression' on 'XPathEvaluator': The string './data/repeat31/calculate41/' is not a valid XPath expression.

Tried a couple of things to see if Enketo will play nice and also not break Collect/WebForms again.

  • flipping the filter has no effect on Enketo loading (doesn't) or Web Forms working (still does)
  • replacing the current/field filter with the entire instance lookup....WORKS EVERYWHERE :confetti_ball: :partying_face:
    • join(', ' , ${repeat5}[finding_parent = current()/${calculate39}]/calc_finding_ID)

    • join(', ' , ${repeat5}[finding_parent = instance('parent')/root/item[name != ''][position()=position(current()/..)]/name]/calc_finding_ID)

1 Like