Loop & Merge: Randomly show a question in some loop iterations but hide in other iterations | XM Community
Solved

Loop & Merge: Randomly show a question in some loop iterations but hide in other iterations


Hi there,
I have a Loop & Merge block in which I want a certain question to be displayed in only some loop iterations but not always. To this end, I'm trying to build upon this question and answer. However, I need to match

${lm://CurrentLoopNumber}
to several loop iteration numbers, so that if
${lm://CurrentLoopNumber}
equals either (for example)
2
or
4
or
10
, then a question will be hidden in the 2nd, 4th, and 10th loop iterations. To do this, I found this nice solution, which allowed me to write the following code:
{

/*Place your JavaScript here to run when the page loads*/



var foo = "${lm://CurrentLoopNumber}";

var L = function()

{

  var obj = {};

  for(var i=0; i
    obj[arguments[i]] = null;




  return obj;

};



if(foo in L(2,4,10)) jQuery("#"+this.questionId).hide();

if(foo in L(2,4,10)) jQuery("#Buttons").hide();




});




Qualtrics.SurveyEngine.addOnReady(function()

{

/*Place your JavaScript here to run when the page is fully displayed*/



var foo = "${lm://CurrentLoopNumber}";

var L = function()

{

  var obj = {};

  for(var i=0; i
    obj[arguments[i]] = null;




  return obj;

};

if(foo in L(2,4,10)) jQuery("#Buttons").hide();

if(foo in L(2,4,10)) jQuery('#NextButton').click();




});

This code works well. Problem is, I do not want the loop iterations to be hardcoded this way. Instead, I want them to be randomly generated for each survey respondent. If my Loop & Merge block has a total of 15 iterations, I want for each respondent to randomly subset 5 iterations from the total 15, and those random 5 will be the iterations in which the question will not be displayed.
I found this solution adequate for randomly subsetting. So what I ended up doing was to do the following randomization in an earlier survey question, then assign it to an embedded data variable:
Qualtrics.SurveyEngine.addOnReady(function()
{
/*Place your JavaScript here to run when the page is fully displayed*/

function getRandomSubarray(arr, size) {
    var shuffled = arr.slice(0), i = arr.length, temp, index;
    while (i--) {
        index = Math.floor((i + 1) * Math.random());
        temp = shuffled[index];
        shuffled[index] = shuffled[i];
        shuffled[i] = temp;
    }
    return shuffled.slice(0, size);
}


var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var fiveRandomMembers = getRandomSubarray(x, 5);

Qualtrics.SurveyEngine.setEmbeddedData('five_sampled_numbers', fiveRandomMembers);
Then, pass it to the
if
statement in the loop&merge question code:
Qualtrics.SurveyEngine.addOnload(function()
{
/*Place your JavaScript here to run when the page loads*/

var foo = "${lm://CurrentLoopNumber}";
var trials = "${e://Field/five_sampled_numbers}";

var L = function()
{
    var obj = {};
    for(var i=0; i        obj[arguments[i]] = null;


    return obj;
};

if(foo in L(trials)) jQuery("#"+this.questionId).hide();
if(foo in L(trials)) jQuery("#Buttons").hide();


});


Qualtrics.SurveyEngine.addOnReady(function()
{
/*Place your JavaScript here to run when the page is fully displayed*/

var foo = "${lm://CurrentLoopNumber}";
var trials = "${e://Field/five_sampled_numbers}";

var L = function()
{
    var obj = {};
    for(var i=0; i        obj[arguments[i]] = null;


    return obj;
};


if(foo in L(trials)) jQuery("#Buttons").hide();
if(foo in L(trials)) jQuery('#NextButton').click();




});



But it doesn't work. Any ideas?

Thanks,
Emmanuel

icon

Best answer by emmanuel 25 May 2020, 15:57

View original

7 replies

Userlevel 6
Badge +21

Hi emmanuel ,
Below is the option in Loop and Merge which allow us to show the subset of questions out of total.
Present only [#] of total Loops.pngLoop & Merge - Qualtrics Support
Hope this information may help you.

Hi PraDeepKotian_Ugam,


thanks for responding. Unfortunately, what you are suggesting doesn't address my problem. I don't need to subset the total number of loop iterations, but only a specific question within the loop. The rest of the questions should be displayed in all loops.
Emmanuel

Userlevel 7
Badge +22

Once you have five_sampled_numbers embedded data set, You can add display logic to those questions as below:
image.png

Hi rondev ,
Thanks for your answer. I fell in love with this solution, and I almost celebrated this, but then I realized that display logic based on

contains
is problematic because if
five_sampled_numbers
includes, for example: 11, 12, 13,14, 15, then for the display logic, loop numbers 1, 2, 3, 4, 5 satisfy the logic. 11-15 contain the digit 1, 12 contains the digit 2, 13 contains the digit 3, etc. So even though neither 1, 2, 3, 4, or 5 were part of
five_sampled_numbers
, loop numbers 1-5 would nevertheless be displayed because they satisfy the logic's matching criteria.
Is there a way to circumvent this?
Thanks,
Emmanuel

Userlevel 7
Badge +22

Yes, I too thought the same hence I tested it before posting here. Can you check the same and confirm.

rondev --
Yes, I wrote my comment after testing it and witnessing that it didn't give me the desired result. But you are saying that for you it was working OK? If so, it's very strange.
I will also note that I've been still trying to figure out a solution through JavaScript, and interestingly, the JavaScript code I use fails for the exact same reason as the solution you proposed: when matching

${lm://CurrentLoopNumber}
to the array resulted from the 5 sampled numbers, the matching returns
TRUE
as long as
${lm://CurrentLoopNumber}
matches ANY digit in the array of 5 sampled numbers, even if it doesn't represent a correct match. The only time it returns
FALSE
is when
${lm://CurrentLoopNumber}
doesn't match any digit in the set whatsoever. When I tested the JS code out of Qualtrics, the matching was working fine; meaning, only proper exact matches returned
TRUE
. To me this just adds confusion, how could it possibly be that same JS code is behaving differently. And display logic acting up too?

Solution found. The problem was with the line:
var trials = "${e://Field/five_sampled_numbers}";
trials , after the above assignment, contains a string of characters rather than an array of numbers. This means that using a function that operates on arrays would not work. The solution is to convert

"${e://Field/five_sampled_numbers}
to an array of numbers once its retrieved from embedded data.
This solution came after someone answered my question at StackOverflow: https://stackoverflow.com/a/61985522/6105259

For anyone who might be interested in this in the future, here's the code I ended up using. It works perfectly.

First, in a question that comes before the Loop & Merge block, insert the following JS code:
Qualtrics.SurveyEngine.addOnReady(function()
{
/*Place your JavaScript here to run when the page is fully displayed*/

// the following function randomly subsets a list of numbers
// inspired by: https://stackoverflow.com/a/11935263/6105259
    function getRandomSubarray(arr, size) {
    var shuffled = arr.slice(0), i = arr.length, temp, index;
    while (i--) {
        index = Math.floor((i + 1) * Math.random());
        temp = shuffled[index];
        shuffled[index] = shuffled[i];
        shuffled[i] = temp;
    }
    return shuffled.slice(0, size);
}


// we want to define the total number of trials in the block. in this case we have 30 trials
var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30];


// say that we want a question to appear in only 30% of trials. therefore, for a total of 30 trials, the question will appear only in 10. in other words, the question will be hidden in 20 trials. let's randomly subset the 20 trials where the question will be hidden, sing the function defined above:
var twentyRandomMembers = getRandomSubarray(x, 20);


// finally, assign the resultant list of 20 numbers to an embedded data variable
Qualtrics.SurveyEngine.setEmbeddedData('twenty_sampled_numbers_block_one', twentyRandomMembers);

Then, in the question you'd like to sometimes show/hide (within the loop & merge block), insert the JS code:
Qualtrics.SurveyEngine.addOnload(function()
{   
    var currentLoopNum = "${lm://CurrentLoopNumber}"; // assign value of current loop number into a JS variable
    currentLoopNum = parseInt(currentLoopNum, 10); // convert the value from character to numeric
    
    var hideQuestionInTrials = "${e://Field/twenty_sampled_numbers_block_one}";
    hideQuestionInTrials = hideQuestionInTrials.split(",").map(function (n) { return parseInt(n, 10); }); // convert the value from string of characters to array of numbers


    if (hideQuestionInTrials.includes(currentLoopNum)) jQuery("#"+this.questionId).hide(); // hide the question using an if-statement: if current loop number (aka, "trial") is included in the list of 20-to-be-hidden -> then hide
    if (hideQuestionInTrials.includes(currentLoopNum)) jQuery("#Buttons").hide(); // hide the buttons (Next and Previous) using the same if-statement as above


});


Qualtrics.SurveyEngine.addOnReady(function()
{   
// reassign variables, same as above
    var currentLoopNum = "${lm://CurrentLoopNumber}";
    currentLoopNum = parseInt(currentLoopNum, 10);
    
    var hideQuestionInTrials = "${e://Field/twenty_sampled_numbers_block_one}";
    hideQuestionInTrials = hideQuestionInTrials.split(",").map(function (n) { return parseInt(n, 10); });
    
// similar if-statements as above
    if (hideQuestionInTrials.includes(currentLoopNum)) jQuery("#Buttons").hide();  // keep hiding buttons
    if (hideQuestionInTrials.includes(currentLoopNum)) jQuery('#NextButton').click(); // auto-advance to Next page
    
});

That's it. Good luck.

Leave a Reply