Reply To: SepWithN + random item from template

PennController for IBEX Forums Support SepWithN + random item from template Reply To: SepWithN + random item from template

#10916
bta
Participant

I have figured out a solution, which I’m sharing below (I’ve put this code in a .js file that I added under “modules”).

I use it in my Sequence() as in Sequence( sepChunkWithChunk("theSeparator", 1, "theStimuli", 2) ), which does what I described in my previous post (*except that it doesn’t add a separator after the final stimulus, which I did on purpose*).


// this code below lets you put something like sepChunkWithChunk("separators", 1, "stimuli", 2) into the Sequence
//      this works so that, if you have a Trial (in a Template) named "separators" and a Trial (in a Template) named "stimuli", …
//      …it will present trials from the "stimuli" in chunks of 2, with each chunk separated by 1 trial from "separators"
//		(NOTE: there will be a "separators" chunk between chunks of "stimuli" — meaning if there are 4 chunks of "stimuli", only 3 "separators" chunks will be alreadyUsed)
function  SepChunkWithChunk(sep, nSep, main, nMain) {
	assert(typeof(nSep)=="number" && nSep>0, "nSep should be a number greater than 0");
	assert(typeof(nMain)=="number" && nMain>0, "nMain should be a number greater than 0");

    this.args = [sep, main];

    this.run = function(arrays) {
		var alreadyUsedSep = [];
		var alreadyUsedMain = [];
		let sep = arrays[0];
		let main = arrays[1];
		var numChunksForMain = Math.ceil(main.length / nMain); // calculates the number of chunks of 'main' are necessary to show all items from 'main'
		var totalNumUsedFromMain = 0; // counter for tallying number of items used from 'main'
		
        var chunkedAndSeparatedArray = []; // this array will house the order of all trials, randomized into chunks of 'main' separated by chunks of 'sep'

		for (let i = 1; i <= numChunksForMain; ++i) { // run this loop as many times as there are chunks of 'main'
			counterMain = 0;
			counterSep = 0;

			// create a chunk with 'nMain' elements by looping 'nMain' times
			//		STOP LOOPING if it's already pulled out all trials from 'main'
			//		(this will happen if, e.g., you have 8 trials in 'main' and 'nMain' is set to something like 5 — two chunks are needed, but the second chunk should only have 3 elements)
			while (counterMain < nMain && totalNumUsedFromMain < main.length) {
				// generate a random number between 0 and (main.length-1):
				r = Math.floor(Math.random()*main.length);
				
				// keep generating a new random number until the random number generated hasn't been used before
				//		(this has the effect of blocking the repetition of items from 'main' that have already been added to 'chunkedAndSeparatedArray')
				while (alreadyUsedMain.includes(r)) { r = Math.floor(Math.random()*main.length); }
				
				// now that a unique random number has been generated, add it to the array of random numbers that have been used
				alreadyUsedMain.push(r);
				
				// add the rTH trial from 'main' to 'chunkedAndSeparatedArray'
				chunkedAndSeparatedArray.push(main[r]);
				
				// increase counters 
				++counterMain;
				++totalNumUsedFromMain;
			}

			// create a chunk with 'nSep' elements by looping 'nSep' times
			//		DON'T RUN THIS LOOP if the last chunk of 'main' has just been added to 'chunkedAndSeparatedArray'
			if (i != (numChunksForMain)){
			    while (counterSep < nSep) {
				// generate a random number between 0 and (sep.length-1):
				r = Math.floor(Math.random()*sep.length);
				
				// keep generating a new random number until the random number generated hasn't been used before
				//		UNLESS all the trials from 'sep' have already been used
				//		(this has the effect of allowing separator trials to be re-used just in case the number of separators needed is greater than the number of unique trials in 'sep')
				while (alreadyUsedSep.includes(r) && sep.length > alreadyUsedSep.length ) { 
					r = Math.floor(Math.random()*sep.length);
				}

				// now that a unique random number has been generated, add it to the array of random numbers that have been used
				alreadyUsedSep.push(r);

				// add the rTH trial from 'sep' to 'chunkedAndSeparatedArray'
				chunkedAndSeparatedArray.push(sep[r]);

				// increase counter
				++counterSep;
			    }
			}
		}

        // return the finalized Array, which will have all the trials of 'main' in a raondomized order
        // moreover, between every chunk of 'nMain' trials, there is a chunk of random 'nSep' trials from 'sep'
        return chunkedAndSeparatedArray;
    }
}
function sepChunkWithChunk(sep, nSep, main, nMain) { return new SepChunkWithChunk(sep, nSep, main, nMain); }