Have an idea?

Visit Sawtooth Software Feedback to share your ideas on how we can improve our products.

Populating Variables in a Loop

I am creating a travel diary in as a loop. It goes as follows (will look similar).

Q1: Which days of the week do you commute to work (checkbox)
--this feeds into the loop list and goes from Sunday (1) to Saturday (7)

Loop begins
Q3: was your commute mode for day 2 the same as day 1?
 --If they answer 'yes' to this the next loop is skipped.
Q2: what were your commute modes for day 1 (checkbox)

The loop is a bit interesting.  Q3 is actually the first question in the loop, but I have a post skip on Q1 to always skip to Q2. I have a post skip on Q3 to skip to the next loop iteration if Q3 == 2 (yes).

Upon exiting the loopI am trying to count the number of times a respondent drives alone. so Q2_1 == 1. The initial difficulty was if they say 'yes' to Q3 then they skip the next loop, but I found out I fix that by doing the following in perl:
if (GETVALUE("Q3.2") == 2 )     {  # 2 is 'yes"

I thought this worked then realized it only works if a respondent commutes on consecutive days only. If a respondent commutes on Monday, Wednesday and Friday then the script does not work. Here is a scenario:

The first loop iteration is skipped because the respondent does not commute on Sunday

Q3.2 - always skipped due to the post-skip on Q1

Q2_1.2 == 1

The third loop iteration is skipped because the respondent does not commute on Tuesday

Q3.4  == 2 - respondent's commute mode on Wednesday is the same as it was on Monday. Respondent skips to the next loop iteration.

The fifth loop iteration is skipped because the respondent does not commute on Thursday.

Q3.6 == 2  - respondent's commute mode on Friday is the same as it was on Monday. Respondent skips to the next loop iteration....this is the last loop iteration. Respondent exits the loop.

Now, the code above does not work and the variables for iterations 4 and 6 are blank.  I need somethig like the following but cannot figure it out.

if (GETVALUE("Q3.thisIteration") == 2 )     {  

I have been toying with using the loop functions and building some type of constructed list, but cannot wrap my head around it.  Help me, you're my only hope.
asked Sep 7, 2021 by Nwiggin Bronze (1,785 points)

1 Answer

0 votes
If we use the ListValue function with the list that is being looped, we can get the previous non-skipped iteration.  Does this get you going?

my $iteration = LOOPITERATION('loop1');
if (GETVALUE('Q3.' . $iteration) == 2) {
    my $previousLoopValue = LISTVALUE('list1', $iteration - 1);
    SETVALUE('Q2.' . $iteration, GETVALUE('Q2.' . $previousLoopValue));
answered Sep 8, 2021 by Zachary Platinum Sawtooth Software, Inc. (205,575 points)
Hey Zach,

I've been working on this for a while and am unable to get it to behave how I want, but think I'm close.  

I don't believe that I can do it inside the loop because the only place I could put the code is in Q3. The reason for this is if Q3==2 then it skips to the next loop iteration.

So, I started trying to work outside the loop.

I created a series of temp variables called willItBlend1 thru 7. I am then setting up a perl loop where i can be anywhere from 1 to 7.  This loop is pretty simple. It is checking each iteration of Q3 (e.g. Q3.1, Q3.2, Q3.3 etc.) and if a particular iteration == 2 then we populate 'willItBlend' with the value of $i.  

for (my $i = 1; $i <= 7; $i++) {

        if (GETVALUE('Q3.'    .    $i)      ==     2)    {
            SETVALUE('willItBlend'    .    $i    ,    $i);

That is just a short proof of concept test, but does not get me where I want to be.  What I want to accomplish is if Q3.x == 2 then populate 'willItBlend' with the value (of i?) from the previous iteration. I have been trying to do that by adding line 2 and modifying line 4 in the code below:

for (my $i = 1; $i <= 7; $i++) {
         my $previous = LISTVALUE("myConstructedList"    ,    $i);
            if (GETVALUE('Q3.'    .    $i)    ==    2)    {
                SETVALUE('willItBlend'    .    $i    ,    $previous );

What I was thinking this would do is the following:
willItBlend1    =    NEVER populated
willitBlend2    =    1 (if Q3.2==2, else blank)
willitBlend3    =    2 (if Q3.3==2, else blank)
willitBlend4    =    3 (if Q3.4==2, else blank)
willitBlend5    =    4 (if Q3.5==2, else blank)
willitBlend6    =    5 (if Q3.6==2, else blank)
willitBlend7    =    6 (if Q3.7==2, else blank)

Instead, what I'm getting is nothing at all like I'm expecting. For this example I went through iterations 2, 4, 6, and 7 and provided the following data
Q3.4 == 2
Q3.6 == 2
Q3.7 == 2

Given this, I would expect willItBlend to look like the following:
willItBlend4 == 2 (the first iteration seen)
willItBlend6 == 4
willItBlend7 == 6

Rather all I am seeing is willItBlend4 == 7. And I have no idea where this is coming from.  I'm fairly certain it has to do with the creation of $previous and I'm just missing something there.

The end goal of this is to say, if Q3.5 == 2 then SetValue(Q1.5,Q1.lastIterationSeen).  So, if they say, "yeah, I commuted the same way on Wednesday as I did on Monday" I want to auto-populate Wednesday with the data from Monday.

for this
Could you just do the work in the skip logic that skips the respondent to the next loop iteration?  Something like this:

Begin Unverified Perl
my $iteration = LOOPITERATION('loop1');
if (GETVALUE('Q3.' . $iteration) == 2) {
    my $previousLoopValue = LISTVALUE('list1', $iteration - 1);
    SETVALUE('Q2.' . $iteration, GETVALUE('Q2.' . $previousLoopValue));
return ...;
End Unverified

where the "..." is the regular skip logic.
I believe it works, but follow-up question:
In your code you are defining $previousLoopValue within the if statement. Is there a reason for that?

That aside, I believe I got it working. I had no idea you could use perl inside the skip logic!   I had to modify the code a bit and I'm putting it below (and in VSC) for reference.

Begin Unverified Perl
my $iteration = LOOPITERATION('loop1');
my $loopValue = LOOPVALUE('loop1');
my $previousLoopValue = LISTVALUE('list1', $iteration - 1);
if (GETVALUE('Q3.' . $loopValue) == 2) {
           SETVALUE('Q2.' . $loopValue, GETVALUE('Q2.' . $previousLoopValue));
return GETVALUE("COMM5") == 2;
End Unverified

for some reason using the code you originally sent would actually populate $previousLoopValue as if it was ADDING 1 (it would populate with the next value) very strange.  By creating $loopValue i was able to get it to work.
I considered just putting the ListValue call in the same line as the SetValue call, but figured I'd split it up for better readability.  Putting it either inside or outside the conditional will work and won't have significant effect either way, but I do think inside the loop is marginally better from a programming principles perspective.  If it's inside, the work of calling ListValue will only happen if the GetValue check returns true; if it's outside, ListValue has to run everytime.  It's just generally good practice to limit scope as much as possible - if a variable is only used within a conditional or a loop, I'd rather put the variable inside rather than outside.

Your LoopValue change looks correct to me.  Glad to hear you've got it working.