PennController for IBEX › Forums › Support › SepWithN + random item from template › Reply To: SepWithN + random item from template
October 21, 2023 at 2:56 pm
#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); }