1

I am trying to develop a little test for an educational website and I want to randomize the order of the questions and exercises and later down the line the order of the answers under each question with a set of radio buttons. I have tried the code I found on two separate questions (namely this one and this one), although both the answers were practically the same. The code that I am using does what I want to some extent, but parts of my test are missing sometimes (most of the time I get 5 questions and 2-3 exercises instead of 6 and 4). How do I correct this? Full code sample in this JSFiddle (some irrelevant things from back-end are there, ignore them - content is placeholder to make debugging easier), as well as the javascript/jQuery code on its own below:

$(document).ready(function () {
        var questions = $('.question');
        for (var i = 0; i < questions.length; i++) {
            var target = Math.floor(Math.random() * questions.length - 1) + 1;
            var target2 = Math.floor(Math.random() * questions.length - 1) + 1;
            questions.eq(target).before(questions.eq(target2));
        }

        var exercises = $(".exercise");
        for (var j = 0; j < exercises.length; j++) {
            var target = Math.floor(Math.random() * exercises.length - 1) + 1;
            var target2 = Math.floor(Math.random() * exercises.length - 1) + 1;
            exercises.eq(target).before(exercises.eq(target2));
        }
    });

P.S. #1: The website's back-end is built with Asp.Net and C#, if that has anything to do with the problem.

P.S. #2: Run the fiddle four or five times and count the number of questions and exercises to reproduce the problem.

Community
  • 1
  • 1
Angelos Chalaris
  • 6,611
  • 8
  • 49
  • 75

2 Answers2

2

I changed your code so that target will not be same as target2:

JavaScript

var questions = $('.question');
for (var i = 0; i < questions.length; i++) {
  var target = Math.floor(Math.random() * questions.length - 1) + 1;
  var target2 = target;
  while(target2 === target) {
    target2 = Math.floor(Math.random() * questions.length - 1) + 1;
  }
  questions.eq(target).before(questions.eq(target2));
}

var exercises = $(".exercise");
for (var j = 0; j < exercises.length; j++) {
  var target = Math.floor(Math.random() * exercises.length - 1) + 1;
  var target2 = target;
  while(target2 === target) {
    target2 = Math.floor(Math.random() * exercises.length - 1) + 1;
  }
  exercises.eq(target).before(exercises.eq(target2));
}

fiddle

Arg0n
  • 8,283
  • 2
  • 21
  • 38
1

I'm going to assume all the .question / .exercise elements are in the same parent.

They way I'd approach it would be:

  1. Grab the questions/exercises as an array and detach them

  2. Randomize the array, in any of serveral ways addressed in this question's answers

  3. Append them

For instance:

function randomize(selector) {
  // Get the elements and their parent
  var elements = $(selector);
  var parent = elements.eq(1).parent();
  
  // Detach, get array
  elements = elements.detach().get();
  
  // Shuffle it
  shuffle(elements);
  
  // Attach
  parent.append(elements);
}

// From https://stackoverflow.com/a/2450976/157247
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

randomize(".question");
randomize(".exercise");
<ul>
  <li class="question">Question 1</li>
  <li class="question">Question 2</li>
  <li class="question">Question 3</li>
  <li class="question">Question 4</li>
  <li class="question">Question 5</li>
</ul>
Exercises:
<ul>
  <li class="exercise">Exercise 1</li>
  <li class="exercise">Exercise 2</li>
  <li class="exercise">Exercise 3</li>
  <li class="exercise">Exercise 4</li>
  <li class="exercise">Exercise 5</li>
  <li class="exercise">Exercise 6</li>
</ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks a lot! This works like a charm, I suppose I can then run it again on the different questions' radio buttons set using a selector based on the `name` attribute. – Angelos Chalaris Jun 27 '16 at 11:07
  • **Why this is not the accepted answer:** I believe this method to be superior to my original code as it is more generalised and allows for a more versatile solution that can work without rewriting any code, however the issue at hand was that my code had some problem I could not figure out and I needed to know what that was and how to fix it. Therefore I am accepting @Arg0n's answer as it was more enlightening in my case and taught me something more valuable. – Angelos Chalaris Jun 27 '16 at 11:15
  • @AngelosChalaris: And fair enough! :-) – T.J. Crowder Jun 27 '16 at 12:36