JS for selecting two random responses from a multiple choice question

More
4 years 6 months ago #143121 by dieterdubinsky
Hello dear LimeSurvey-Community,

thank you for doing awesome work and the effort you put into this tool.

I have a question regarding random selection of items from a multiple choice question:
My survey has some items listed in a multiple choice question. Now I want to use up to two of the checked items as placeholders/for piping in the following questions. Googling around I found this:

manual.limesurvey.org/Workarounds:_Manip...estion_for_later_use

which works out just fine (LS 2.5) and was very very helpful. However, it only works for one item. Would there be a way to rewrite the script so it picks two different (!) random items out of all the checked ones and writes them in two different hidden textboxes, so I can use these "answers" in following questions?

I tried doing some work on the script, but obviously did not get very far, as I'm not experienced with JS. I thought it might have something to do with the requirement given in the workaround manual, as it says "Immediately following that question, create a short-text question"
The topic has been locked.
More
4 years 6 months ago #143131 by jelo
Feel free to support a feature request for building dynamic list, which would allow doing random picks without workarounds.
bugs.limesurvey.org/view.php?id=11688

I'm in a love/hate relationship with JavaScript.So no solution by me.But should be possible to add another random pick when the first randomly chosen item is removed from the array before choosing the next item.

The meaning of the word "stable" for users
www.limesurvey.org/forum/development/117...ord-stable-for-users
The topic has been locked.
More
4 years 6 months ago - 4 years 6 months ago #143138 by Deusdeorum
You could probably modify that last part "Load the hidden question with a random item from the array" to something like this:
		var checkedLength = checkedAnswers.length;
		hiddenInput1 = checkedAnswers[Math.floor(Math.random()*checkedLength)];
		if (checkedLength > 1) {
    		   do {
      		      hiddenInput2 = checkedAnswers[Math.floor(Math.random()*checkedLength)];
    		   } while(hiddenInput1 == hiddenInput2);
		}
 
		$(hiddenInput).val(hiddenInput1+" "+hiddenInput2);

This will fill the short text field to "Answer_X Answer_Y" so you could use regex to set condition on that short text field ((regexMatch("/Answer_X/", short_text_field_question))). Not tested though :)
Last edit: 4 years 6 months ago by Deusdeorum.
The topic has been locked.
More
4 years 6 months ago #143140 by tpartner
Instead of the short-text question, I would insert a multiple-short-text with 2 subquestions and shuffle the array something like this:

<script type="text/javascript" charset="utf-8">		
	$(document).ready(function() {	
 
		// Identify the questions
		var thisQuestion = $('#question{QID}');
		var qHidden = thisQuestion.nextAll('.multiple-short-txt:eq(0)');
		var hiddenInput1 = $('input.text:eq(0)', qHidden);
		var hiddenInput2 = $('input.text:eq(1)', qHidden);
 
		// Hide qHidden
		qHidden.hide();
 
		// Class for "Other
		$('input.text', thisQuestion).closest('.answer-item').addClass('other-item');
 
		// Listener on the checkboxes
		$('input.checkbox', thisQuestion).on('change', function(e) {
			handleChecked();
		});
 
		// Listener on the "Other" input
		$('input.text', thisQuestion).on('keyup change', function(e) {
			setTimeout(function() {
				handleChecked();
			}, 250);
		});
 
		function handleChecked() {
			// Build an array of checked answers
			var checkedAnswers = [];
			$('input.checkbox:checked', thisQuestion).each(function(i) {
				if($(this).closest('.answer-item').hasClass('other-item')) {
					checkedAnswers.push($(this).closest('.answer-item').find('input.text').val());
				}
				else {
					checkedAnswers.push($.trim($(this).nextAll('.label-text:eq(0)').text()));
				}				
			});
 
			// Shuffle the array
			shuffleArray(checkedAnswers);
 
			// Load the hidden question with a random 2 items from the array
			$(hiddenInput1).val(checkedAnswers[0]);
			if(checkedAnswers.length > 1) {
				$(hiddenInput2).val(checkedAnswers[1]);
			}
			else {
				$(hiddenInput2).val('');
			}
 
			// Fire Expression Manager
			checkconditions(hiddenInput1.value, hiddenInput1.name, hiddenInput1.type);
			checkconditions(hiddenInput2.value, hiddenInput2.name, hiddenInput2.type);
		}
    });
 
	function shuffleArray(array) {
		for (var i = array.length - 1; i > 0; i--) {
			var j = Math.floor(Math.random() * (i + 1));
			var temp = array[i];
			array[i] = array[j];
			array[j] = temp;
		}
		return array;
	}
</script>

Sample survey attached:

File Attachment:

File Name: limesurvey...4794.lss
File Size:20 KB

Cheers,
Tony Partner

Solutions, code and workarounds presented in these forums are given without any warranty, implied or otherwise.
Attachments:
The topic has been locked.
More
4 years 6 months ago - 4 years 6 months ago #143212 by dieterdubinsky
Wow guys, thanks a lot for the quick support :). The solution given by Tony worked perfectly, you're a genius. I really can't thank you enough.

One more question: Would a kind of script like this also be possible for matrix questions instead of multiple choice? I can't even imagine what that would look like, must be really complicated. If not, I'll just work with what you gave me, that should do as well!

Edit: some more info on that scenario - the question would be "which of the following do you know?" and the options would be "Don't know"; "I know that one (or something like that)" and "I went there during the last xy". The "filter" for the random selection would only apply to those items where the last option was checked.

Thanks again for the help!
Last edit: 4 years 6 months ago by dieterdubinsky.
The topic has been locked.
More
4 years 6 months ago #143218 by tpartner
This will load the hidden multi-text question with the array row labels where the last column is checked.

<script type="text/javascript" charset="utf-8">	$(document).ready(function() {	
 
		// Identify the questions
		var thisQuestion = $('#question{QID}');
		var qHidden = thisQuestion.nextAll('.multiple-short-txt:eq(0)');
		var hiddenInput1 = $('input.text:eq(0)', qHidden);
		var hiddenInput2 = $('input.text:eq(1)', qHidden);
 
		// Hide qHidden
		qHidden.hide();
 
		// Listener on the radios
		$('input.radio', thisQuestion).on('click', function(e) {
			handleChecked();
		});
 
		function handleChecked() {
			// Build an array of checked answers
			var checkedAnswers = [];
			$('input.radio:checked', thisQuestion).each(function(i) {
				if($(this).closest('td.answer-item').is(':last-child')) {
					checkedAnswers.push($(this).closest('tr.answers-list').find('.answertext').text());
				}				
			});
 
			// Shuffle the array
			shuffleArray(checkedAnswers);
 
			// Load the hidden question with 2 random items from the array
			$(hiddenInput1).val(checkedAnswers[0]);
			if(checkedAnswers.length > 1) {
				$(hiddenInput2).val(checkedAnswers[1]);
			}
			else {
				$(hiddenInput2).val('');
			}
 
			// Fire Expression Manager
			checkconditions(hiddenInput1.value, hiddenInput1.name, hiddenInput1.type);
			checkconditions(hiddenInput2.value, hiddenInput2.name, hiddenInput2.type);
		}
    });
 
	function shuffleArray(array) {
		for (var i = array.length - 1; i > 0; i--) {
			var j = Math.floor(Math.random() * (i + 1));
			var temp = array[i];
			array[i] = array[j];
			array[j] = temp;
		}
		return array;
	}
</script>

Sample survey attached:

File Attachment:

File Name: limesurvey...94-2.lss
File Size:21 KB

Cheers,
Tony Partner

Solutions, code and workarounds presented in these forums are given without any warranty, implied or otherwise.
Attachments:
The topic has been locked.
More
4 years 6 months ago - 4 years 6 months ago #143220 by dieterdubinsky

This will load the hidden multi-text question with the array row labels where the last column is checked.

<script type="text/javascript" charset="utf-8">	$(document).ready(function() {	
 
		// Identify the questions
		var thisQuestion = $('#question{QID}');
		var qHidden = thisQuestion.nextAll('.multiple-short-txt:eq(0)');
		var hiddenInput1 = $('input.text:eq(0)', qHidden);
		var hiddenInput2 = $('input.text:eq(1)', qHidden);
 
		// Hide qHidden
		qHidden.hide();
 
		// Listener on the radios
		$('input.radio', thisQuestion).on('click', function(e) {
			handleChecked();
		});
 
		function handleChecked() {
			// Build an array of checked answers
			var checkedAnswers = [];
			$('input.radio:checked', thisQuestion).each(function(i) {
				if($(this).closest('td.answer-item').is(':last-child')) {
					checkedAnswers.push($(this).closest('tr.answers-list').find('.answertext').text());
				}				
			});
 
			// Shuffle the array
			shuffleArray(checkedAnswers);
 
			// Load the hidden question with 2 random items from the array
			$(hiddenInput1).val(checkedAnswers[0]);
			if(checkedAnswers.length > 1) {
				$(hiddenInput2).val(checkedAnswers[1]);
			}
			else {
				$(hiddenInput2).val('');
			}
 
			// Fire Expression Manager
			checkconditions(hiddenInput1.value, hiddenInput1.name, hiddenInput1.type);
			checkconditions(hiddenInput2.value, hiddenInput2.name, hiddenInput2.type);
		}
    });
 
	function shuffleArray(array) {
		for (var i = array.length - 1; i > 0; i--) {
			var j = Math.floor(Math.random() * (i + 1));
			var temp = array[i];
			array[i] = array[j];
			array[j] = temp;
		}
		return array;
	}
</script>

Sample survey attached:

File Attachment:

File Name: limesurvey...94-2.lss
File Size:21 KB


Thanks again, Tony :) You are a lifesaver! I will try to implement that instead of the multiple choice question.

In the meantime, another issue with the survey came up: Sometimes when I test the survey and I only claim to know one of the items, the variable qHidden is empty and the item is written in qHidden2 or vice versa. So if I only need to show one block of questions because the participant only knows one option, he or she would sometimes get questions with empty placeholders {qHidden_1} or {qHidden_2}. Is there any chance you also know something about this?

Edit: What I am trying to do is have two blocks of items each dealing with oneof the given options. I want to set a condition with "count" that the second block is only displayed if there were actually two or more items checked.
Last edit: 4 years 6 months ago by dieterdubinsky.
The topic has been locked.
More
4 years 6 months ago #143233 by tpartner

Sometimes when I test the survey and I only claim to know one of the items, the variable qHidden is empty and the item is written in qHidden2 or vice versa.

If there is only one eligible option, it will be written to the first sub-question of the hidden question.

Cheers,
Tony Partner

Solutions, code and workarounds presented in these forums are given without any warranty, implied or otherwise.
The topic has been locked.
More
4 years 6 months ago - 4 years 6 months ago #143235 by dieterdubinsky

Sometimes when I test the survey and I only claim to know one of the items, the variable qHidden is empty and the item is written in qHidden2 or vice versa.

If there is only one eligible option, it will be written to the first sub-question of the hidden question.

Alright, thank you :) I probably messed up somewhere along the way. I'll have a thorough look again and hope that I can find it.

Thanks a lot for helping me out with this. This is an awesome community!
Last edit: 4 years 6 months ago by dieterdubinsky.
The topic has been locked.
More
4 years 6 months ago #143393 by dieterdubinsky
Hello, it's me again :).

I'm still working on the survey and was wondering if it would be possible to set some kind of quota regarding the placeholders. For example, can the short text field inputs for qHidden_1 and qHidden_2 also be transferred to a hidden MC question where you could set a quota? Or, alternatively, could you just input a hidden MC question with all possible options where the items would only be visible if they were chosen for qHidden? And then maybe check both that are still visible and set a quota for that?

Imagine having a list like this:

What do you like?
- Apples
- Bananas
- Strawberries
- Grapefruits
- Grapes

And you would want two of those chosen at random, but only about 200 people who get asked regarding "Apples" (as many people would choose apples), so you won't have a sample of 1000 people where 800 went with apples and only 100 with grapefruits.

Would that be possible with some JS workaround? I read about hiding single choice questions etc., but I couldn't find anything on placeholders.
The topic has been locked.
More
4 years 6 months ago #143405 by tpartner
Search the forum for "hidden multiple-choice question". You should find lots of tips on how to toggle them.

Cheers,
Tony Partner

Solutions, code and workarounds presented in these forums are given without any warranty, implied or otherwise.
The topic has been locked.
More
4 years 6 months ago #143421 by dieterdubinsky
Thank you again, I'll try my best. If I find anything that works, I will post it here.
The topic has been locked.
More
4 years 6 months ago - 4 years 6 months ago #143471 by dieterdubinsky
Hello again guys :) another thank you to Tony for helping me with this. I actually found a solution to take my two hidden text field variables and put them into two hidden single choice questions which can then be used for quotas (if anyone else is looking for this).

I hope I am allowed to post an external link here. I found this script: www.aptigence.com.au/wordpress/category/limesurvey/

which is actually based on numerical inputs but can be changed to text inputs. So, this is what I put into both of the single choice questions (Please note that they have to be on a different page than the hidden text questions (e.g. in another group in group-by-group-mode) and as far as I can tell there has to be some kind of visible question on the same group, but I'm not sure about that):
$(document).ready(function() {
  /* define variable for this question */
  var thisQuestion = $('#question{QID}');
 
  /* Hide it */
		thisQuestion.hide();
 
  /* This gets the text input that was put into the first hidden text field by Tony's script */
 var text="{INSERTANS:999999X253X31041}"; /* Change this to match your first hidden text question SGQ */
 
  /* NOW UPDATE THE LIST QUESTION */
  var updateName='#answer999999X254X3105'; /* Change this to match your first list question SGQ */
  var updateCode='';
  if(text == 'apples') {
     updateCode='A';
  } else if (text == 'bananas') {
     updateCode='B';
  } else if (text == 'strawberries') {
     updateCode='C';  
  } else if (text == 'grapefruit') {
     updateCode='D';
  } 
  $(updateName+updateCode).attr("checked","checked"); /* First we "check" the radio button */
  $(updateName+updateCode).click();  /* Then we fake the mouse click, to fire off any conditions in LimeSurvey */
  $(updateName+updateCode).change(); /* Then we fake the change, to also make sure any conditions are applied */
});
</script>

For this, the single choice question answer codes have to be A, B, C, D. Credits of course go to jcleeland from Aptigence.

This is still not ideal as both randomly selected items are on two different questions so I kind of have to guess the quota behind each one. I'll doctor around some more in hopes of finding a solution for a single MC question that checks both answers for use in quotas.
Last edit: 4 years 6 months ago by dieterdubinsky.
The topic has been locked.
More
4 years 6 months ago - 4 years 6 months ago #143474 by tpartner
To facilitate placing everything on a single page, here is an extension of my array workaround above with an added hidden multiple-choice question.

The multiple-choice question must be placed directly after the hidden multi-text and must have the same sub-questions and codes as the array question. The check-boxes corresponding the the hidden random texts will be checked.

<script type="text/javascript" charset="utf-8">	
	$(document).ready(function() {	
 
		// Identify the questions
		var thisQuestion = $('#question{QID}');
		var thisQuestionID = {QID};
		var qHidden = thisQuestion.nextAll('.multiple-short-txt:eq(0)');
		var hiddenInput1 = $('input.text:eq(0)', qHidden);
		var hiddenInput2 = $('input.text:eq(1)', qHidden);
		var qHidden2 = thisQuestion.nextAll('.multiple-opt:eq(0)');
		var qHidden2ID = qHidden2.attr('id').replace(/question/, '');
 
		// Hide the control questions
		qHidden.hide();
		qHidden2.hide();
 
		// Listener on the radios
		$('input.radio', thisQuestion).on('click', function(e) {
			handleChecked();
		});
 
		function handleChecked() {
			// Build an array of checked sub-question codes
			var checkedAnswers = [];
			$('input.radio:checked', thisQuestion).each(function(i) {
				if($(this).closest('td.answer-item').is(':last-child')) {
					var thisSQCode = $(this).closest('tr.answers-list').attr('id').split('X'+thisQuestionID)[1];
					checkedAnswers.push(thisSQCode);
				}				
			});
 
			// Shuffle the array
			shuffleArray(checkedAnswers);
 
			// Reset the hidden multi-choice
			$('input.checkbox', qHidden2).each(function(i) {
				$(this).prop('checked', false);
				$(this).nextAll('input:hidden:eq(0)').val('');
			});
 
			// Load the hidden questions with 2 random items from the array
			$(hiddenInput1).val($.trim($('tr.answers-list[id$="X'+thisQuestionID+checkedAnswers[0]+'"] .answertext').text()));
			$('input.checkbox[id$="X'+qHidden2ID+checkedAnswers[0]+'"]', qHidden2).trigger('click');
			if(checkedAnswers.length > 1) {
				$(hiddenInput2).val($.trim($('tr.answers-list[id$="X'+thisQuestionID+checkedAnswers[1]+'"] .answertext').text()));
				$('input.checkbox[id$="X'+qHidden2ID+checkedAnswers[1]+'"]', qHidden2).trigger('click');
			}
			else {
				$(hiddenInput2).val('');
			}
 
			// Fire Expression Manager
			checkconditions(hiddenInput1.value, hiddenInput1.name, hiddenInput1.type);
			checkconditions(hiddenInput2.value, hiddenInput2.name, hiddenInput2.type);
			$('input.checkbox', qHidden2).each(function(i) {
				checkconditions(this.value, this.name, this.type);
			});
		}
    });
 
	function shuffleArray(array) {
		for (var i = array.length - 1; i > 0; i--) {
			var j = Math.floor(Math.random() * (i + 1));
			var temp = array[i];
			array[i] = array[j];
			array[j] = temp;
		}
		return array;
	}
</script>

Sample question attached:

File Attachment:

File Name: limesurvey...4421.lss
File Size:25 KB

Cheers,
Tony Partner

Solutions, code and workarounds presented in these forums are given without any warranty, implied or otherwise.
Attachments:
Last edit: 4 years 6 months ago by tpartner.
The topic has been locked.
More
4 years 6 months ago #143479 by dieterdubinsky
Wow! Again, I can't thank you enough. I just copied that over to a sample survey and it worked like a charm.

If it's not too much to ask: Could that script also be rewritten to also work for an MC question instead of an array? I know I had it the other way round before, but all the structures and relevance equations are now build upon this single MC question where the randomization is pulled from. If it is too much to ask I of course understand, I would then try to rebuild most of the survey with that array question as base.

It would look like this: MC question where the participant checks the items he knows --> two hidden text questions with two randomly selected items of those he knows --> hidden MC question that checks both items given in the text questions.
The topic has been locked.

Start now!

Just create your account and start using Limesurvey today.

Register now