Javascript - Merge Sort Visualizer using CSS Style to Sort, having issues

Viewed 295

I am having an issue with my merge sort visualizer.

My program has no issues visualizing bubble sort or quick sort, as I can do the swapping operation of css property values in-place, but I am having major issues trying to get merge sort to work properly. The issue arises when I try to update a css property on the dom, it causes the sort to not function.

I have tried passing in copies of the data I wish to sort, and all sorts of weird things I could think of to make it work. I am currently trying to sort by the css property 'maxWidth'. I use that to display how large a div element is in the html file and then visualize the sort from there.

My latest thought has been to set all the div elements to have another css property equal to the maxWidth (I am using fontSize as it does not affect my program) and then sorting based on fontSize, allowing me in theory to change the maxWidth properties of the divs without affecting merge sorts algorithm.

I am including my entire js file as I hope reading my correctly working bubble sort or quick sort functions can help you see what I am trying to achieve. Thank you so much for taking the time to read this and offer any help!

Important Note: I am not trying to visualize the individual steps of merge sort yet because I am unable to update the final result to the html page without affecting the merge sort algorithm. According to console logs, my merge sort algorithm does indeed work, I just can't update the DOM without messing it up. Once I can do that, I will turn it into an asynchronous function using async and await like I previously did with bubble and quick sort.

/********* Generate and Store Divs to be Sorted *************/
const generateSortingDivs = (numOfDivs) => {
    const divContainer = document.querySelector('.div-container');
    let html = '';
    for (let i = 0; i < numOfDivs; i++) {
        let r = Math.floor(Math.random() * 100);
        html += `<div class='sorting-div' id='id-${i}' style='max-width: ${r}%'>&nbsp</div>`;
    }
    divContainer.innerHTML = html;
    for(let i = 0; i < numOfDivs; i++) {
        let x = document.getElementById('id-' + i);
        x.style.fontSize = x.style.maxWidth;
    }
}

const storeSortingDivs = () => {
    const divContainer = document.querySelector('.div-container');
    let divCollection = [];
    const numOfDivs = divContainer.childElementCount;

    for(let i=0; i<numOfDivs; i++) {
        let div = document.getElementById('id-' + i);
        divCollection.push(div);
    }

    return divCollection;
}

/********** SLEEP FUNCTION ************/
//Used to allow asynchronous visualizations of synchronous tasks
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/******* SWAP FUNCTIONS *********/
//Used for Testing Algorithm before Animating Visualization
const syncSwap = (div1, div2) => {
    let tmp = div1.style.maxWidth;
    div1.style.maxWidth = div2.style.maxWidth;
    div2.style.maxWidth = tmp;
}

async function asyncSwap(div1, div2) {
    await sleep(50);
    let tmp = div1.style.maxWidth;
    div1.style.maxWidth = div2.style.maxWidth;
    div2.style.maxWidth = tmp;
}

const swapDivs = (smallerDiv, biggerDiv) => {
    return new Promise(resolve => {
        setTimeout(() => {
            let tmp = smallerDiv.style.maxWidth;
            smallerDiv.style.maxWidth = biggerDiv.style.maxWidth;
            biggerDiv.style.maxWidth = tmp;
            resolve();
        }, 50);
    });
}

/****************************************/
/*********** SORTING ALGO'S *************/
/****************************************/

/******* BUBBLE SORT ***********/
async function bubbleSort(divCollection) {
    displayBubbleSortInfo();
    const len = divCollection.length;
    for(let i=0; i<len; i++) {
        for(let j=0; j<len-i-1; j++) {
            divCollection[j].style.backgroundColor = "#FF4949";
            divCollection[j+1].style.backgroundColor = "#FF4949";

            let numDiv1 = parseInt(divCollection[j].style.maxWidth);
            let numDiv2 = parseInt(divCollection[j+1].style.maxWidth);
            let div1 = divCollection[j];
            let div2 = divCollection[j+1];
            if(numDiv1 > numDiv2) {
                await swapDivs(div2, div1);
            }
            divCollection[j].style.backgroundColor = "darkcyan";
            divCollection[j+1].style.backgroundColor = "darkcyan";
        }
        divCollection[len - i - 1].style.backgroundColor = 'black';
    }
}

function displayBubbleSortInfo(){
    const infoDiv = document.querySelector('.algo-info');
    let html = `<h1>Bubble Sort Visualizer</h1>`;
    html += `<h2>Time Complexity: O(n^2)</h2>`;
    html += `<h3>Space Complexity: O(1)</h3>`;
    html += `<p>This sorting algorithm loops through the array and continues to push the 
               largest found element into the last position, also pushing the last available
               position down by one on each iteration. It is guaranteed to run in exactly 
               O(n^2) time because it is a nested loop that runs completely through.</p>`;
    infoDiv.innerHTML = html;
}

/****** QUICK SORT ********/
async function quickSort(divCollection, start, end) {
    if(start >= end) return;

    let partitionIndex = await partition(divCollection, start, end);

    await Promise.all([quickSort(divCollection, start, partitionIndex - 1), quickSort(divCollection, partitionIndex + 1, end)]);
}

/* This function takes last element as pivot, places
   the pivot element at its correct position in sorted
    array, and places all smaller (smaller than pivot)
   to left of pivot and all greater elements to right
   of pivot */
async function partition(divCollection, start, end) {
    let pivotIndex = start;
    let pivotValue = parseInt(divCollection[end].style.maxWidth);
    for(let i = start; i < end; i++) {
        if(parseInt(divCollection[i].style.maxWidth) < pivotValue) {
            await asyncSwap(divCollection[i], divCollection[pivotIndex]);
            pivotIndex++;
        }
    }
    await asyncSwap(divCollection[pivotIndex], divCollection[end]);
    return pivotIndex;
}

function displayQuickSortInfo(){
    const infoDiv = document.querySelector('.algo-info');
    let html = `<h1>Quick Sort Visualizer</h1>`;
    html += `<h2>Time Complexity: O(n log n)</h2>`;
    html += `<h3>Space Complexity: O(log n)</h3>`;
    html += `<p>This sorting algorithm uses the idea of a partition to sort
                each iteration recursively. You can implement quick sort
                in a variety of manners based on the method in which you
                pick your "pivot" value to partition the array. In this
                visualization, I implemented the method that chooses the 
                last element of the array as the pivot value. You could
                also choose the first value, the middle value, or the median
                value based on the first, middle, and last values.</p>`;
    infoDiv.innerHTML = html;
}

/* Merge Sort does not sort in place, and thus we have to be
*  clever when implementing it and also editing the css style
*  of our divs to show the visualization of how the algorithm
*  works. My method is to store a copy of the divs, that way
*  I can use one to be sorted by merge sort, and the other to
*  change the css style property to show the visualization.
*  Unlike Quick Sort and Bubble Sort, we are not swapping
*  elements when sorting, instead we are merging entire
*  arrays together as the name implies.                     */
function mergeSort(divCollection) {
    if(divCollection.length < 2) return divCollection;
    let middleIndex = Math.floor(divCollection.length / 2);
    let left = divCollection.slice(0, middleIndex);
    let right = divCollection.slice(middleIndex);
    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    let mergedCollection = [];
    while(left.length && right.length) {
        if(parseInt(left[0].style.fontSize) < parseInt(right[0].style.fontSize || right.length === 0)) {
            let el = left.shift();
            mergedCollection.push(el);
        } else {
            let el = right.shift();
            mergedCollection.push(el);
        }
    }

    let res = mergedCollection.concat(left.slice().concat(right.slice()));

    return res;
}


/***** INITIALIZATION FUNCTION *******/
generateSortingDivs(10);
let divs = storeSortingDivs();
let copyDivs = [...divs];
console.log('Original State: ')
console.log(divs);
//bubbleSort(divs);
//displayQuickSortInfo();
//quickSort(divs, 0, divs.length-1);
let x = mergeSort(copyDivs);
console.log('Sorted: ');
console.log(x);
0 Answers
Related