Indexed-repeat based on response to a separate question

I have what I think is a simple question about indexed-repeat that I can't figure out.

In the attached form, I have a repeat group of respondents that enter two responses ('num' and 'letter'). After the end of the repeat group, I want to find the repeat group member with the highest value of 'num' and then save that person's response to 'letter' into a new variable via calculate. The crux of my problem is that I don't know the parameters for indexed-repeat to tell ODK to find a specific member, who could be located anywhere within the index.

Is there a simple way to accomplish this? My real application is slightly more complicated, but not significantly so.

indextest.xlsx (10.9 KB)

Interesting one! indexed-repeat is a "convenience" function that allows for a limited type of form querying that requires knowing the index ahead of time. You can see its documentation here.

For more complex queries, you can use XPath expressions. In this case, you can use something like /indextest/resp_repeat[num = ${max_num}]/letter. I have a full form here and fixed your constraint while I was there.

A couple things to note -- that actually gives you the name of the letter in the letter choice list, not the label. I tried to get the label using jr:choice-name but it doesn't seem to work. jr:choice-name may not work with filter expressions, I'm not sure. The other thing is that this only works if there is only one response with the max value. As far as I know, there isn't a way around that with ODK's XPath subset.

If anyone has ideas for overcoming those limitations, I'd love to see them!

I have an idea:
Step 1. You add 1 more calculate inside the repeat to define which repeat is max num:
if(${num} = ${max_num}, ${letter}, '')
Step 2. You join the letter of max_num in the above calculate OUTSIDE the repeat, i.e. after the end repeat (in this example, I name it 'mark_letter'):
join(' ', ${mark_letter})
So you can get only 1 letter or more letters, depending on how many num are max.
@LN: you are right, jr:choice-name doesnot work with choice filter.

1 Like

Thanks to you both for your ideas!

Helene: that form is perfect. In fact, the limitations you mentioned are actually not a problem for me, because -- in my actual application -- I want the number from the choice list rather than the label. So, in at least one sense, my example was more complex than my actual application. For my purposes, this works great.

On the point about getting the choice name, this very ugly option would work: rather than using the jr:choice-name approach you used in calculate, use a nested if statement in calculate:

if(${max_num_letter_choice}='1',"A",if(${max_num_letter_choice}='2',"B","Or some other letter or more if statements"))

This would get really long if you had many possible choices, but it would still work.

Thiendao: That's an interesting idea. I may be misunderstanding, but wouldn't this statement:

if(${num} = ${max_num}, ${letter}, '')

always evaluate to the letter for the first person in the loop? That is to say, the first person's 'num' would always equal 'max_num', since they are the only person who has entered a number so far. Again, I might be misunderstanding!

Thanks again to you both!

1 Like

Unfortunately, a few years have passed and this solution no longer seems to work. I have not used it for awhile, but I know Helene's (@LN) former solution worked, because I ran a project that used it.

Now when I try to upload Helene's form to Ona or use the XLSForm Online, I get the following error:

"This field is repeated:
${resp_repeat}[1]/letter[1];${resp_repeat}[2]/letter[1]
You may need to use the indexed-repeat() function to specify which value you want."

I am open to any ideas of how to resolve this, but indexed-repeat does not seem to be the way.

Previously, /indextest/resp_repeat[num = ${max_num}] was being treated as a single node when there was only one repeat with num matching the maximum value. Now it appears to always be treated as a nodeset even if there's only one match. You can address this by explicitly specifying the first match: ${resp_repeat}[num = ${max_num}][position()=1]/letter. I've adjusted the sample form accordingly. This is something that would have worked previously as well and would have addressed the case in which multiple people picked the same favorite number.

We've made a number of changes to evaluation to improve performance and increase correctness. I can't immediately identify what change would lead to this behavior but will keep thinking about it.

Thank you, Helene. This is really helpful. I don't often use this approach, but it is very helpful for having a kish grid in which we fully list all members of the household and then select from a random subset of those household members who meet some specific criteria. Glad that it still works with only a very slight modification!

1 Like