<h1>Merge Sort</h1>

<div id='buttonContainer'>
  <button id="randomize">Randomize</button>
  <button id="sort">Sort</button>
</div>

<div>
  <label for="slider">Delay in seconds</label>
  <input type="range" id="slider" value="1" min="0" max="3" step="0.5">
  <span id="seconds">1</span>
</div>

<div id="spanContainer">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div id="resultsContainer">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<code>Best -- Time: O(n log(n)) Space: O(n)</code>
<code>Ave -- Time: O(n log(n)) Space: O(n)</code>
<code>Worst -- Time: O(n log(n)) Space: O(n)</code>
* {
  font-family: Arial;
  font-size: 3.5vw;
  outline: none;
}

body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  margin: 0;
  height: 100vh;
}

h1 {
  margin: 0 0 12.5vh;
  font-size: 6vw;
  font-weight: 400;
}

#buttonContainer {
  display: flex;
  justify-content: center;
}

button {
  width: 25vw;
  padding: 1vw;
  margin: 0.5vw 0.5vw 1.5vw;
  border: 1px solid black;
  border-radius: 25vw;
  background-color: white;
  cursor: pointer;
  transition: all 0.2s ease;
}

button:hover {
  background-color: black;
  color: white;
  box-shadow: 0 0 3.5vw gray;
}

#slider {
  margin-right: 10px;
}

#seconds {
  position: absolute;
}

#spanContainer {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 12.5vh 0 3vh;
}

#resultsContainer {
  display: flex;
  justify-content: center;
  align-items: center; 
}

#spanContainer span, #resultsContainer span {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 3.5vw;
  height: 3.5vw;
  margin: 0 0.5vw;
  padding: 1.5vw;
  border: 1px solid black;
  border-radius: 3.5vw;
}

/* ----------------------------------------------
 * Generated by Animista on 2020-5-30 8:25:12
 * Licensed under FreeBSD License.
 * Edited by Kent Warren
 * See http://animista.net/license for more info. 
 * w: http://animista.net, t: @cssanimista
 * ---------------------------------------------- */

/**
 * ----------------------------------------
 * animation pulsate-fwd
 * ----------------------------------------
 */

.pulsate-fwd {
	-webkit-animation: pulsate-fwd 0.75s ease-in-out 3 both;
	        animation: pulsate-fwd 0.75s ease-in-out 3 both;
}

@-webkit-keyframes pulsate-fwd {
  0% {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
  50% {
    -webkit-transform: scale(1.1);
            transform: scale(1.1);
           box-shadow: 0 0 1vw gray;
  }
  100% {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
}

@keyframes pulsate-fwd {
  0% {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
  50% {
    -webkit-transform: scale(1.1);
            transform: scale(1.1);
           box-shadow: 0 0 1vw gray;
  }
  100% {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
}

code {
  font-family: monospace;
  margin: 3vh 0 0;
}

code:first-of-type {
  margin: 12.5vh 0 0;
}

sup {
  font-family: monospace;
}
console.clear()

// randomize button
const randomize = document.getElementById('randomize');
// sort button
const sort = document.getElementById('sort');
// delay input
let delay = document.getElementById('slider');
// delay value
let delayValue = delay.value * 1000;
// delay span
const delaySpan = document.getElementById('seconds');
// for when sort is in process
let sortInProcess = false;
// div of spans with random numbers
const randomSpans = [...document.getElementById('spanContainer').children];
// div of spans for results
const resultSpans = [...document.getElementById('resultsContainer').children];


// randomize
randomize.addEventListener('click', () => {
  // if in progress, return
  if (sortInProcess) return;
  
  randomSpans.forEach((span, i) => {
    span.innerText = Math.floor( Math.random() * 100 );
    // reset resultSpans
    resultSpans[i].innerText = '';
    resultSpans[i].style.cssText = '';
    resultSpans[i].className = '';
  });
});



// sort
sort.addEventListener('click', async () => {
  // if in progress or no random numbers yet or all green, return
  if (sortInProcess || randomSpans[0].innerText === '' || resultSpans[0].style.color === 'green') return;
  sortInProcess = true;
  
  // start merge sort
  await mergeSort(randomSpans);
  
  // revert state
  sortInProcess = false;
  // reset execution context count
  executionContext = 0;
});



// delay
delay.addEventListener('input', (e) => {
  delaySpan.innerText = e.target.value;
  delayValue = e.target.value * 1000;
});


// use execution context count for basis of index position of sorted nums
let executionContext = 0;

async function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  executionContext++;
  
  const mid = Math.floor( arr.length / 2 );
  // async functions return a Promise, so await for resolve
  const left = await mergeSort(arr.slice(0, mid));
  const right = await mergeSort(arr.slice(mid));
  
  // sort
  const sorted = mergeTwoSortedArrays(left, right);
  
  // update DOM
  const index = (sorted.length === 10 || executionContext <= 5) ? 0 : 5;
  sorted.forEach((span, i) => {
    // add randomSpan CSS
    if (sorted.length !== 10) span.style.cssText = 'color: red; box-shadow: 0 0 1vw gray;';
    // add innerText
    resultSpans[i + index].innerText = span.innerText;
    // show green if complete
    if (sorted.length === 10) {
      resultSpans[i].style.color = 'green';
      resultSpans[i].className = 'pulsate-fwd';
    }
  });
  
  // delay for effect
  await new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, delayValue);
  }); 
  
  // remove randomSpan CSS
  sorted.forEach(span => span.style.cssText = '');
  return sorted;
}

function mergeTwoSortedArrays(arrL, arrR) {
  let l = 0;
  let r = 0;
  let result = [];
  
  // while both have length, push into result in order
  while (l < arrL.length && r < arrR.length) {
    if (Number(arrL[l].innerText) <= Number(arrR[r].innerText)) {
      result.push(arrL[l]);
      l++;
    } else {
      result.push(arrR[r]);
      r++;
    }
  }
  
  // only one of these will run
  while (l < arrL.length) {
    result.push(arrL[l]);
    l++;
  }
  
  while (r < arrR.length) {
    result.push(arrR[r]);
    r++;
  }  

  return result;
}



/*

Unadulterated merge sort


function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  const mid = Math.floor( arr.length / 2 );
  const left = mergeSort(arr.slice(0, mid));
  const right = mergeSort(arr.slice(mid));
  
  return mergeTwoSortedArrays(left, right);
}


function mergeTwoSortedArrays(arrL, arrR) {
  let l = 0;
  let r = 0;
  let result = [];
  
  // while both have length, push into result
  while (l < arrL.length && r < arrR.length) {
    if (arrL[l] <= arrR[r]) {
      result.push(arrL[l]);
      l++;
    } else {
      result.push(arrR[r]);
      r++;
    }
  }
  
  // only one of these will run
  while (l < arrL.length) {
    result.push(arrL[l]);
    l++;
  }
  
  while (r < arrR.length) {
    result.push(arrR[r]);
    r++;
  }
  
  return result;
}

*/

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.