Quicksort Algorithm

sorted grains depicting quicksort algorithm
JavaScript
  /*** GLOBALS ***/
  
  var morphArray;
  var numberRange = 500;
  var prints = 21;
  var waitTime = 100;

  /*** FUNCTIONS ***/

  /*create the actual array to be sorted*/
  function createArray() {
    morphArray = [];
    for (i = 0; i < prints; i++) {
      var number = Math.floor((Math.random() * numberRange));
      morphArray.push(number);
    }
    var uniqueArray = Array.from(new Set(morphArray));
    //test that every number in array is unique
    if (JSON.stringify(uniqueArray) === JSON.stringify(morphArray)) {
      return morphArray
    } else {
      //recreate the array if each number is not unique
      return createArray();
    }
  }

  /*create the visual representation of the array - a set of boxes*/
  function createBoxes() {
    morphArray = createArray();
    for (i = 0; i < prints; i++) {
      var randomNumber = morphArray[i];
      //append each box to the container; wrapper is necessary to assign different transition durations for float effect (class "wrap") and rotate effect (class "box") transforms
      $('.box-container').append('<div class="wrap float"><div class="box invisible unsorted"><div class="number-text">' + randomNumber + '</div></div></div>');
      //assign unique data attribute to each box
      $('.wrap:eq(' + i + ')>.box').attr('data-num', randomNumber);
      /* 
      ADD CLASS "VISIBLE" TO EACH BOX TRIGGERING ENTRANCE ANIMATION
      -IIFE is used to create execution context so the current iteration's context is remembered when time comes to add "visible" class.  This enables the setTimeout to work before the execution context expires and the loop is completed.
      -The outer IIFE provides delay b/t creation of each box and addition of animation class, so element exists long enough for animation to kick in successfully.  This only seemed necessary for the first box.
      -The inner IIFE does the work of adding the class to each box after the delay period   
      */
      (function buyTime(i) {
        setTimeout(function () {

          (function triggerAnimation(i) {
            setTimeout(function () {
              var currentBox = '.wrap:eq(' + i + ')>.box';
              $(currentBox).toggleClass('visible invisible');
            }, waitTime * i);
          })(i);

        }, waitTime);
      })(i);
    }
  }

  /*quicksort function*/

  //add method quicksort to Array.prototype that exists in browser
  Array.prototype.quicksort = function () {

    //"this" is equal to the array on which the quicksort function is executed
    var r = this;
    // base case - return the array when it is one digit, and thus doesn't need more sorting
    if (r.length <= 1) {
      return r;
    }

    //create pivot of 1 digit that is just past mid-point of the array, removing it from the array
    var pivot = r.splice(Math.floor(r.length / 2), 1);

    //initialize sub-arrays for each side of the pivot
    var less = [];
    var greater = [];

    //run number of times there are items left in the array after pivot is removed
    for (i = r.length - 1; i >= 0; i--) {
      if (r[i] < pivot) {
        less.push(r[i]);
      } else {
        greater.push(r[i]);
      }
    }

    var c = [];

    //recursive cases - sort each sub array
    return c.concat(less.quicksort(), pivot, greater.quicksort());
  }

  /***EVENTS***/

  /* click event for "Create Array" button */
  $('.create-array-btn').click(function () {

    var allVisible = true;

    //turn allVisible false if any .box is missing .visible (entrance animation) class 
    $('.wrap').each(function () {
      if ($(this).children().hasClass('visible') === false) {
        allVisible = false;
      };
    });

    //create boxes if there are none
    if ($('.wrap').length === 0) {
      createBoxes();
    } else if ($('.wrap').length === prints && allVisible === true) {
      $('.wrap').remove();
      createBoxes();
    } //or remove and recreate boxes only if a prior array with its boxes has finished displaying  
  });

  /* click event for "Quick Sort" button */
  $('.sort-btn').click(function () {

    var readyToSort = false;

    //if the full number of prints exist, we are provisionally ready to sort
    if ($('.box-container').get(0).childElementCount === prints) {
      readyToSort = true;
    }

    //but don't sort if all the boxes are not finished animating onto the screen, or if they're already sorted
    $('.box').each(function () {
      if ($(this).hasClass('invisible') || $(this).hasClass('sorted')) {
        readyToSort = false;
      }
    });

    if (readyToSort === true) {
      morphArray = morphArray.quicksort();

      /*move first box into position of the sorted morphArray*/
      var sortedDataAttr = morphArray[0];
      //data attribute must be absolutely unique for fetching it based on that to work
      var sortedElement = '*[data-num="' + sortedDataAttr + '"]';
      $('.box-container').prepend($(sortedElement).parent());
      $(sortedElement).toggleClass('sorted unsorted');

      /*move remaining boxes into their postion in the sorted morphArray*/
      for (i = 1; i < prints; i++) {

        (function (i) {
          setTimeout(function () {
            var sortedDataAttr = morphArray[i];
            var sortedElement = '*[data-num="' + sortedDataAttr + '"]';
            //place box after the last instance of .sorted
            $('.sorted:last').parent().after($(sortedElement).parent());
            $(sortedElement).toggleClass('sorted unsorted');
          }, waitTime * i);
        })(i);
      }
    }
  });
HTML
<div class="button-container">
  <button class="create-array-btn wpforms-submit">Create Array</button>
  <button class="sort-btn wpforms-submit   ">Quick Sort</button>
</div>
<div class="box-container"></div>
CSS
.button-container {
  display: flex;
  justify-content: center;
  margin-bottom: 18px;
}

@media(max-width:500px) {
  .button-container {
    flex-direction: column;
    align-items: center;
  }
  .sort-btn {
    margin-top: 7px;
  }
}

@media(min-width:501px) {
  .sort-btn {
    margin-left: 7px;
  }
}

.box-container {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.box {
  margin: 15px;
  justify-content: center;
  align-items: center;
  width: 130px;
  height: 130px;
  font-family: Montserrat, sans-serif;
  font-size: 30px;
  display: flex;
  transition: opacity .5s, transform .5s;
}

.invisible {
  opacity: 0;
  transform: rotate(-90deg);
}

.visible {
  opacity: 1;
  transform: rotate(0deg);
}

.unsorted {
  background: #4d4d4d;
}

.sorted {
  background: black;
}

.number-text {
  font-size: 24px;
  color: white;
}

/*float effect on hover*/

.float {
  transition-duration: .3s;
  transition-property: transform;
  transition-timing-function: cubic-bezier(0.09, 0.51, 0.83, 0.96);
}

.float:active,
.float:focus,
.float:hover {
  transform: translateY(-8px)
}

Leave a Reply

Your email address will not be published. Required fields are marked *