Xpath and Dynamic default in a repeat

Maybe it's a basic issue but I can't make it work.
I want to iterate over 100 item (from 1 to 100).
By default I want the value to be set by the position in the loop, so at the first iteration, 1 is set as the default value, 2 at the second iteration and so on. But sometimes, the enumerator will have to jump over many values (for example from point 2 to 25).
The round after 25 I now want to set 26 as the default value.
I tried to do that with a select_one and with an integer question without any success.

I am stuck with this calculation that works fine, but can't be used as a default value (cycle detected) :

if(position(..)=1,position(..),indexed-repeat(${point_id}, ${points_repeat}, position(..)-1)+1)

At the first loop, I want to set 1 as the default, and after, when position(..) is not 1, I want to use last value +1

Am I missing something obvious or is it just not possible ?

contact_points_protocols.xlsx (466,3 Ko)

Thanks for your help !

I have a workaround in mind : ask if the expected value is good or not. And if not ask for an input.

1 Like

Hi @mathieubossaert ! :smile:

This way?
contact_points_protocols.xlsx (577.1 KB)

Great Day!

1 Like

Thanks a lot @MinimalPotato !
This side of xlsform is still unclear for me.

What would be the expression if point_id question was within a group inside the repeat ?

Hi @mathieubossaert ! :waving_hand:

I’ve prepared three examples to help explain how the same functions under different group/nested group configurations:

  1. Type 1: All variables inside a repeat group are placed within a single sub-group (let’s call it sub-group G1).
    XLSForm:
    example 1 - contact_points_protocols.xlsx (577.3 KB)

  2. Type 2: All variables inside a repeat group are in a single sub-group (sub-group G1), but point_id is placed in another sub-group (sub-group G2) nested under G1.
    XLSForm:
    example 2 - contact_points_protocols.xlsx (577.6 KB)

  3. Type 3: point_id inside the repeat group is placed directly in a separate sub-group (sub-group G2).
    XLSForm:
    example 3 - contact_points_protocols.xlsx (577.4 KB)

P.S. Apologies for the delayed response! I've ill lately and ended up sleeping through the entire weekend. :sweat_smile:

Let me know! Have a great day! :smile:

1 Like

:smile: Even you were not ill, it was the week-end !

Thanks a lot for the working examples !
I understand that I do not use / understand well the position() function. I will investigate a little bit th documentation !

1 Like

You're most welcome!! :smile:

I'd recommend having a look at this section in the documentation - it does a decent job explaining the logic behind how things are working in the above examples I shared:
:link: https://docs.getodk.org/form-logic/#advanced-xpath-paths

It should help with a clearer picture of what's going on! :+1:

1 Like

I'm with @mathieubossaert, repeat logic confuses the heck out of me, more examples like these are clearer than the documentation as it stands.

@MinimalPotato I saw you use position(../..) to get the repeat position from inside a group in the repeat, I understood that if you had nested repeats (outer & inner) and you wanted the position of the outer from the inner then you would use position(../..), but never thought that a group in a repeat would affect the value of position(..) as the group doesn't repeat, but then thought that perhaps adjacent multiple groups would increment their position value?

I just tested (Enketo only) calculating position(../..) vs position(..) inside a group inside a repeat and then created a second group inside a repeat and again calculated position(..), and the position value returned in a group never changes from 1. Then tested a group in a group in a repeat, a group outside all repeats, and outside all groups/repeats and they were all = 1.

  • position(..) [will always be 1]
  • group0
    • position(..) [will always be 1]
  • repeat outer [Count: 1]
    • position(..) = 1
    • group1
      • position(..) = 1 [will always be 1]
      • position(../..) = 1
      • group1a
        • position(..) = 1 [will always be 1]
    • group2
      • position(..) = 1 [will always be 1]
      • position(../..) = 1
    • repeat inner [Count:1]
      • position(..) = 1
      • position(../..) = 1
    • repeat inner [Count:2]
      • position(..) = 2
      • position(../..) = 1
  • repeat outer [Count: 2]
    • position(..) = 2
    • group1
      • position(..) = 1 [will always be 1]
      • position(../..) = 2
      • group1a
        • position(..) = 1 [will always be 1]
    • group2
      • position(..) = 1 [will always be 1]
      • position(../..) = 2
    • repeat inner [Count:1]
      • position(..) = 1
      • position(../..) = 2
    • repeat inner [Count:2]
      • position(..) = 2
      • position(../..) = 2
2 Likes

Hey @ahblake ! :waving_hand:

Really great analysis!

I completely agree with how tricky XPaths can get, especially when dealing with repeat groups. And yes, the documentation alone doesn't quite cut it when it comes to grasping the complete picture.

In my case, I found it a bit intuitive to understand, probably because it reminded me of recursion in computer science - a concept I spent a lot of time mastering during my graduation. The way recursion breaks problems down into self-similar components really helped frame how these nested repeat structures works - groups within sub-groups, and so on. And at recursion, we're totally expected to rely on our imagination to handle it the way we want. But, again - powerful concept - worth mastering, can totally eliminate loops from any code.

To simplify the idea, we may think of it like this:

  • position(..) -> returns my position in my group
  • position(../..) -> returns my position in my parent’s group
  • position(../../..) -> returns my position in my grandparent’s group

...and so on.

Though, I must admit - I'm starting to get confused by myself now! :sweat_smile:

Thanks again and have a great day! :smile: