'Selection sort animation isn't iterating through the steps of sorting like I intended

Below, I've included the two functions which most likely contain error. The function iterate should pause the program for one second, clear the body of the page, then, for every index in the list colors, create a div with a unique color and height based on a list order. The function selectionSort is a common selection-sort function which, after one iteration of sorting the list order, should execute the function iterate. For some reason, this code produces the final sorted result after one second instead of pausing through every iteration of sorting to display a partially sorted line of divs. Does anyone see any glaring issues with this code? I'll also add that the sleep function itself works properly.

function selectionSort() {
            for (i=0; i<order.length-1; i++) {
                let minIndex = i;
                
                for (j=i+1; j<order.length; j++) {
                    if (order[j] < order[minIndex]) {
                        minIndex = j;
                    }
                }
                
                [order[i], order[minIndex]] = [order[minIndex], order[i]];
                
                iterate();
            }
        }
async function iterate() {
            await sleep(1000);
            document.body.innerHTML = "";
            for (i=0; i<colors.length; i++) {
                let newDiv = document.body.appendChild(document.createElement("div"));
                
                newDiv.style.backgroundColor = colors[order[i]];
                newDiv.style.height = ((order[i] * 2.4) + 36).toString() + "px";
            }
        }

If it helps, this is the full html:

<!DOCTYPE html>
<html>
    <title>Sorting Algorythm</title>
    <head>
        <style>
            body {background: black;}
            div {margin-left: 0; margin-right: 0; display: inline-block; width: 5.5px;}
        </style>
    </head>
    <body></body>
    <script>
        var colors = [];
        let order = [];
        
        setup();
        iterate();
        selectionSort();
        
        function setup() {
            for (i=16; i<256; i++) {
                colors.push("#BB" + i.toString(16) + i.toString(16));
            }
            
            for (i=0; i<colors.length; i++) {
                order.push(i);
            }
            
            shuffle(order);
        }
        
        function selectionSort() {
            for (i=0; i<order.length-1; i++) {
                let minIndex = i;
                
                for (j=i+1; j<order.length; j++) {
                    if (order[j] < order[minIndex]) {
                        minIndex = j;
                    }
                }
                
                [order[i], order[minIndex]] = [order[minIndex], order[i]];
                
                iterate();
            }
        }
        
        async function iterate() {
            await sleep(1000);
            document.body.innerHTML = "";
            for (i=0; i<colors.length; i++) {
                let newDiv = document.body.appendChild(document.createElement("div"));
                
                newDiv.style.backgroundColor = colors[order[i]];
                newDiv.style.height = ((order[i] * 2.4) + 36).toString() + "px";
            }
        }
        
        function shuffle(array) {
            for (i=array.length-1; i>0; i--) {
                let j = Math.floor(Math.random()*(i+1));
                [array[i], array[j]] = [array[j], array[i]];
            }
            return array;
        }
        
        function sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
    </script>
</html>


Solution 1:[1]

You have two main problems:

  1. Every time you have for (i = 0…, you should be declaring i as a variable for that loop instead, like for (let i = 0….

    By doing i = 0 instead of let i = 0 (or var i = 0), you are implicitly creating a global variable i, which means all the loops are accessing and updating the same i at the same time and screwing everything up. You want a separate i for each loop. This also goes for the j inside the nested loop in selectionSort.

  2. Since you are calling iterate() inside a loop in your selectionSort function, and iterate is async, you have to await iterate() if you want a pause between each loop in selectionSort. That also means selectionSort needs to be async as well.

Making those two changes we get this (check your console):

var colors = [];
let order = [];

setup();
iterate();
selectionSort();

function setup() {
  for (let i = 16; i < 256; i++) {
    colors.push("#BB" + i.toString(16) + i.toString(16));
  }

  for (let i = 0; i < colors.length; i++) {
    order.push(i);
  }

  shuffle(order);
}

async function selectionSort() {
  for (let i = 0; i < order.length - 1; i++) {
    let minIndex = i;

    for (let j = i + 1; j < order.length; j++) {
      if (order[j] < order[minIndex]) {
        minIndex = j;
      }
    }

    [order[i], order[minIndex]] = [order[minIndex], order[i]];
    console.log('calling iterate', i);
    await iterate();
  }
}

async function iterate() {
  await sleep(1000);
  document.body.innerHTML = "";
  for (let i = 0; i < colors.length; i++) {
    let newDiv = document.body.appendChild(document.createElement("div"));

    newDiv.style.backgroundColor = colors[order[i]];
    newDiv.style.height = ((order[i] * 2.4) + 36).toString() + "px";
  }
}

function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
body {
  background: black;
}

div {
  margin-left: 0;
  margin-right: 0;
  display: inline-block;
  width: 5.5px;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 cjl750