Skip to content

Instantly share code, notes, and snippets.

@LearningNerd
Last active June 27, 2018 05:21
Show Gist options
  • Save LearningNerd/b27ace0a2ffdca4d9f1394d2b676ca5e to your computer and use it in GitHub Desktop.
Save LearningNerd/b27ace0a2ffdca4d9f1394d2b676ca5e to your computer and use it in GitHub Desktop.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Loops, and stepping through loops one step at a time, triggered by an external event
////////////////////////////////////////////////////////////////////////////////////////////////////
let testArray = [1,2,3,4,5];
////////////////////////////////////////////////////////////////////////////////////////////////////
// FIRST -- a simple while loop, for comparison:
////////////////////////////////////////////////////////////////////////////////////////////////////
let index = 0;
while (index < 5) {
let element = testArray[index];
console.log(index + ": " + element);
// Don't forget to iterate! A while loop makes this more obvious:
index++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 2 -- Advance the loop in stages
// Need a trigger to advance to next step -- in this case, a click:
document.addEventListener("click", stepThroughLoop);
////////////////////////////////////////////////////////////////////////////////////////////////////
///// 2A: Condition inside stepper function
// NOTICE: The code below perfectly mirrors a while loop; just replace the "while" line with the "function" line,
// and the condition would go inside the function (so it will stop running the code after a certain number of iterations)
// (OR: check for the negative condition instead and return to exit the function)
let index = 0;
function stepThroughLoop () {
if (index < 5) {
let element = testArray[index];
console.log(index + ": " + element);
index++;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
///// 2B: No condition; instead of simply iterating, wrap back to the start when end is reached
///// (modular arithmetic, yay!)
// NOTICE: Without a condition, we can cycle through all elements or do infinite looping
let index = 0;
function stepThroughLoop () {
let element = testArray[index];
console.log(index + ": " + element);
// When index reaches last element, wrap back to the start; otherwise just increment
index = (index === testArray.length - 1 ? 0 : index + 1);
// Or keep an incrementer and use remainder operator, as in:
// incrementer++;
// index = incrementer % testArray.length;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// STEPPING THROUGH ***NESTED*** LOOPS
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// FIRST -- A nested while loop for comparison:
// NOTES: - Only need to initialize the outerIndex outside the loop.
// - The innerIndex NEEDS to be defined within the outer loop, before the inner loop.
// - They are indeed NESTED, one loop within the other
let outerIndex = 0;
while (outerIndex < 5) {
let innerIndex = 0;
while (innerIndex < 3) {
console.log(outerIndex + " -- " + innerIndex);
innerIndex++;
}
outerIndex++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 3A -- ADVANCE THROUGH NESTED LOOP IN STAGES
// Notes: - All the incrementers must be initialized outside of the looping function.
// - Using a toggle to switch between running the inner or outer loop.
// - The inner and outer loops' conditional blocks must NOT be nested,
// ...because only ONE of them should increment at a time, never both.
// - In this version, the inner loop's conditional block happens FIRST,
// ...otherwise when the inner loop is done and switching back to the outer,
// ...there will be one iteration that runs NEITHER loop.
// *** This isn't the only way to do it, though! (I'm pretty sure)
// *** Update: after tinkering some more, I'm starting think this IS the only way.
// - The outerIndex increments once BEFORE anything else starts,
// ...so it effectively starts at 1 if initialized at 0.
// - Both loops run one extra time, so if the condition is index < 5, it'll run 6 times.
// *** Why exactly?? Take a closer look at this!
document.addEventListener("click", stepThroughNestedLoop);
let outerIndex = 0;
let innerIndex = 0;
let isInnerLoop = false;
function stepThroughNestedLoop () {
if (innerIndex < 3 && isInnerLoop) {
innerIndex++;
} else if (innerIndex >= 3) {
isInnerLoop = false;
innerIndex = 0;
}
if (outerIndex < 5 && !isInnerLoop ) {
isInnerLoop = true;
outerIndex++;
}
console.log(outerIndex + " -- " + innerIndex);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 3B -- CLEANER, FIXED OFF-BY-ONE ISSUES -- ADVANCE THROUGH NESTED LOOP IN STAGES
// NOTES - Avoiding the initialization off-by-one issue by console.log-ing FIRST instead of last
// - Nested if-statement handles the switch-off from inner to outer loop by resetting
// innerIndex and incrementing outerIndex in the same iteration, so no extra iterations!
// - Better to return early if none of the other code needs to run (This also prevents extra iterations)
document.addEventListener("click", stepThroughNestedLoop);
let outerIndex = 0;
let innerIndex = 0;
let isInnerLoop = true;
function stepThroughNestedLoop () {
if (outerIndex >= 5) {
return;
}
console.log(outerIndex + " -- " + innerIndex);
if (isInnerLoop) {
innerIndex++;
if (innerIndex >= 3) {
isInnerLoop = false;
innerIndex = 0;
}
}
if (!isInnerLoop) {
outerIndex++;
isInnerLoop = true;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// FIXED -- ADVANCE THROUGH NESTED LOOP IN STAGES
////////////////////////////////////////////////////////////////////////////////////////////////////
// FIRST -- An UPDATED nested while loop for comparison:
// NOTES: - I forgot to look at the pattern of *both* indexes in *both* the innter and outer loops!
// - I didn't realize this pattern is true for normal loops as well: for each iteration of the outer loop,
// *both* the outer and inner console.log statements will run, resulting in seeing the same message twice!
// - Example: 0,0 / 0,0 / 0,1 / 0,2 / 1,0 / 1,0 / 1,1 / 1,2 / ...etc...
let outerIndex = 0;
while ( outerIndex < 5 ) {
let innerIndex = 0;
console.log(outerIndex + " -- " + innerIndex);
while ( innerIndex < 3 ) {
console.log(outerIndex + " -- " + innerIndex);
innerIndex++;
}
console.log(outerIndex + " -- " + innerIndex);
outerIndex++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 3C -- FIXED -- ADVANCE THROUGH NESTED LOOP IN STAGES
// NOTES - Split the outer loop section in half, one before and one after the inner loop section,
// so code can run before AND after the inner loop, just like in a normal nested loop.
// - Compared to version 2B, here the outer loop does run FIRST, like a normal nested loop
// - Example: 0,0 / 0,0 / 0,1 / 0,2 / 1,0 / 1,0 / 1,1 / 1,2 / ...etc... < startOuterLoop and innerLoop
document.addEventListener("click", stepThroughNestedLoop);
let outerIndex = 0;
let innerIndex = 0;
let isInnerLoop = false;
function stepThroughNestedLoop () {
if (outerIndex >= 5) {
return;
}
if (!isInnerLoop) {
console.log(outerIndex + " -- " + innerIndex);
isInnerLoop = true;
}
if (isInnerLoop) {
console.log(outerIndex + " -- " + innerIndex);
innerIndex++;
if (innerIndex >= 3) {
isInnerLoop = false;
innerIndex = 0;
}
}
if (!isInnerLoop) {
console.log(outerIndex + " -- " + innerIndex);
outerIndex++;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 3D -- GENERALIZED TEMPLATE -- ADVANCE THROUGH NESTED LOOP IN STAGES
// NOTES - Same exact code as 3C, but parameterized to take functions as inputs,
// separating the looping logic from the code that needs to be looped.
// - NOTE: for now, no parameterization for the incrementors
// - NOTE: Any variables (except indexes) will need to be global
stepThroughNestedLoop (0, 0, triggerF, innerC, outerC, startOuterF, innerF, endOuterF);
// Defining the trigger for step-by-step iteration
// Note: MUST use a loop parameter to hook into the internal looping function
function triggerF (loop) {
document.addEventListener("click", loop);
}
// Defining conditions and code to run for inner and outer loops:
// Note: These next four functions MUST use parameters for indexes
function innerC (innerIndex, outerIndex) {
return innerIndex < 3;
}
function outerC (innerIndex, outerIndex) {
return outerIndex < 5;
}
function startOuterF (innerIndex, outerIndex) {
console.log(outerIndex + " -- " + innerIndex);
}
function innerF (innerIndex, outerIndex) {
console.log(outerIndex + " -- " + innerIndex);
}
function endOuterF (innerIndex, outerIndex) {
console.log(outerIndex + " -- " + innerIndex);
}
function stepThroughNestedLoop (initInnerIndex, initOuterIndex, triggerF, innerC, outerC, startOuterF, innerF, endOuterF) {
let innerIndex = initInnerIndex;
let outerIndex = initOuterIndex;
let isInnerLoop = false;
triggerF(loop);
function loop() {
if ( !outerC(innerIndex, outerIndex) ) {
return;
}
if (!isInnerLoop) {
startOuterF(innerIndex, outerIndex);
isInnerLoop = true;
}
if (isInnerLoop) {
innerF(innerIndex, outerIndex);
innerIndex++;
if ( !innerC(innerIndex, outerIndex) ) {
isInnerLoop = false;
innerIndex = initInnerIndex;
}
}
if (!isInnerLoop) {
endOuterF(innerIndex, outerIndex);
outerIndex++;
}
} //end loop()
} //end stepThroughNestedLoop()
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 3E -- FIXING IT UP SOME MORE (see next sections below)
////////////////////////////////////////////////////////////////////////////////////////////////////
// FIRST -- A nested while loop for comparison:
// NOTES: - Added more console.logs to be more thorough in my comparisons
// - Notice: in the last half of the outer loop, the innerIndex has a value of 3 here!
let outerIndex = 0;
while ( outerIndex < 5 ) {
let innerIndex = 0;
console.log("===============================");
console.log(outerIndex + " -- " + innerIndex);
console.log("===============================");
while ( innerIndex < 3 ) {
console.log("--------------------");
console.log(outerIndex + " -- " + innerIndex);
console.log("--------------------");
innerIndex++;
}
console.log("==================");
console.log(outerIndex + " -- " + innerIndex);
console.log("==================");
outerIndex++;
}
/* OUTPUT:
===============================
0 -- 0
===============================
--------------------
0 -- 0
--------------------
0 -- 1
--------------------
0 -- 2
==================
0 -- 3
==================
===============================
1 -- 0
===============================
--------------------
1 -- 0
--------------------
1 -- 1
--------------------
1 -- 2
==================
1 -- 3
==================
===============================
2 -- 0
===============================
--------------------
2 -- 0
--------------------
2 -- 1
--------------------
2 -- 2
==================
2 -- 3
==================
===============================
3 -- 0
===============================
--------------------
3 -- 0
--------------------
3 -- 1
--------------------
3 -- 2
==================
3 -- 3
==================
===============================
4 -- 0
===============================
--------------------
4 -- 0
--------------------
4 -- 1
--------------------
4 -- 2
==================
4 -- 3
==================
*/
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 3E -- FIXED ONE MORE BUG -- HARD-CODED EXAMPLE OF NEW STEP-THROUGH LOOP
// NOTES: - Easy fix: Now resetting the innerIndex to its initial value at the start of the outer loop,
// just like how it works in the while loop example! (This whole time I've been over-thinking
// this. I should've just copied *every detail* from the while loop example from the get-go!
document.addEventListener("click", stepThroughNestedLoop);
let outerIndex = 0;
let innerIndex = 0;
let isInnerLoop = false;
function stepThroughNestedLoop () {
if (outerIndex >= 5) {
return;
}
if (!isInnerLoop) {
innerIndex = 0;
console.log("===============================");
console.log(outerIndex + " -- " + innerIndex);
console.log("===============================");
isInnerLoop = true;
}
if (isInnerLoop) {
console.log("--------------------");
console.log(outerIndex + " -- " + innerIndex);
console.log("--------------------");
innerIndex++;
if (innerIndex >= 3) {
isInnerLoop = false;
}
}
if (!isInnerLoop) {
console.log("==================");
console.log(outerIndex + " -- " + innerIndex);
console.log("==================");
outerIndex++;
}
}
/* OUTPUT:
===============================
0 -- 0
===============================
--------------------
0 -- 0
--------------------
--------------------
0 -- 1
--------------------
--------------------
0 -- 2
--------------------
==================
0 -- 3
==================
===============================
1 -- 0
===============================
--------------------
1 -- 0
--------------------
--------------------
1 -- 1
--------------------
--------------------
1 -- 2
--------------------
==================
1 -- 3
==================
===============================
2 -- 0
===============================
--------------------
2 -- 0
--------------------
--------------------
2 -- 1
--------------------
--------------------
2 -- 2
--------------------
==================
2 -- 3
==================
===============================
3 -- 0
===============================
--------------------
3 -- 0
--------------------
--------------------
3 -- 1
--------------------
--------------------
3 -- 2
--------------------
==================
3 -- 3
==================
===============================
4 -- 0
===============================
--------------------
4 -- 0
--------------------
--------------------
4 -- 1
--------------------
--------------------
4 -- 2
--------------------
==================
4 -- 3
==================
*/
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 3E -- FIXED ONE MORE BUG -- GENERALIZED STEP-THROUGH LOOP
// NOTES: - Same as above, but the generalized template function.
stepThroughNestedLoop (0, 0, triggerF, innerC, outerC, startOuterF, innerF, endOuterF);
// Defining the trigger for step-by-step iteration
// Note: MUST use a loop parameter to hook into the internal looping function
function triggerF (loop) {
document.addEventListener("click", loop);
}
// Defining conditions and code to run for inner and outer loops:
// Note: These next four functions MUST use parameters for indexes
function innerC (innerIndex, outerIndex) {
return innerIndex < 3;
}
function outerC (innerIndex, outerIndex) {
return outerIndex < 5;
}
function startOuterF (innerIndex, outerIndex) {
console.log("===============================");
console.log(outerIndex + " -- " + innerIndex);
console.log("===============================");
}
function innerF (innerIndex, outerIndex) {
console.log("--------------------");
console.log(outerIndex + " -- " + innerIndex);
console.log("--------------------");
}
function endOuterF (innerIndex, outerIndex) {
console.log("==================");
console.log(outerIndex + " -- " + innerIndex);
console.log("==================");
}
function stepThroughNestedLoop (initInnerIndex, initOuterIndex, triggerF, innerC, outerC, startOuterF, innerF, endOuterF) {
let innerIndex = initInnerIndex;
let outerIndex = initOuterIndex;
let isInnerLoop = false;
triggerF(loop);
function loop() {
if ( !outerC(innerIndex, outerIndex) ) {
return;
}
if (!isInnerLoop) {
innerIndex = initInnerIndex;
startOuterF(innerIndex, outerIndex);
isInnerLoop = true;
}
if (isInnerLoop) {
innerF(innerIndex, outerIndex);
innerIndex++;
if ( !innerC(innerIndex, outerIndex) ) {
isInnerLoop = false;
}
}
if (!isInnerLoop) {
endOuterF(innerIndex, outerIndex);
outerIndex++;
}
} //end loop()
} //end stepThroughNestedLoop()
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 4A -- ONLY RUN ONE STEP AT A TIME -- HARD-CODED EXAMPLE OF NEW STEP-THROUGH LOOP
// NOTES: - All I had to do was add a return statement to the end of the first half of the outer loop!
// - The inner loop and last half of the outer loop do run in the same step, though. I think that's OK.
// - Also made the inner loop condition more specific, to avoid the case where if the last half of the
// outer loop changes the values for the inner loop's condition, the inner loop will run when it shouldn't.
document.addEventListener("click", stepThroughNestedLoop);
let outerIndex = 0;
let innerIndex = 0;
let isInnerLoop = false;
function stepThroughNestedLoop () {
if (outerIndex >= 5) {
return;
}
if (!isInnerLoop) {
innerIndex = 0;
console.log("===============================");
console.log(outerIndex + " -- " + innerIndex);
console.log("===============================");
isInnerLoop = true;
return;
}
if ( isInnerLoop && innerIndex < 3 ) {
console.log("--------------------");
console.log(outerIndex + " -- " + innerIndex);
console.log("--------------------");
innerIndex++;
if (innerIndex >= 3) {
isInnerLoop = false;
}
}
if (!isInnerLoop) {
console.log("==================");
console.log(outerIndex + " -- " + innerIndex);
console.log("==================");
outerIndex++;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// VERSION 4A -- ONLY RUN ONE STEP AT A TIME -- GENERALIZED STEP-THROUGH LOOP
// NOTES: - Same as above, but the generalized template function.
stepThroughNestedLoop (0, 0, triggerF, innerC, outerC, startOuterF, innerF, endOuterF);
// Defining the trigger for step-by-step iteration
// Note: MUST use a loop parameter to hook into the internal looping function
function triggerF (loop) {
document.addEventListener("click", loop);
}
// Defining conditions and code to run for inner and outer loops:
// Note: These next four functions MUST use parameters for indexes
function innerC (innerIndex, outerIndex) {
return innerIndex < 3;
}
function outerC (innerIndex, outerIndex) {
return outerIndex < 5;
}
function startOuterF (innerIndex, outerIndex) {
console.log("===============================");
console.log(outerIndex + " -- " + innerIndex);
console.log("===============================");
}
function innerF (innerIndex, outerIndex) {
console.log("--------------------");
console.log(outerIndex + " -- " + innerIndex);
console.log("--------------------");
}
function endOuterF (innerIndex, outerIndex) {
console.log("==================");
console.log(outerIndex + " -- " + innerIndex);
console.log("==================");
}
function stepThroughNestedLoop (initInnerIndex, initOuterIndex, triggerF, innerC, outerC, startOuterF, innerF, endOuterF) {
let innerIndex = initInnerIndex;
let outerIndex = initOuterIndex;
let isInnerLoop = false;
triggerF(loop);
function loop() {
if ( !outerC(innerIndex, outerIndex) ) {
return;
}
if (!isInnerLoop) {
innerIndex = initInnerIndex;
startOuterF(innerIndex, outerIndex);
isInnerLoop = true;
return;
}
if ( isInnerLoop && innerC(innerIndex, outerIndex) ) {
innerF(innerIndex, outerIndex);
innerIndex++;
if ( !innerC(innerIndex, outerIndex) ) {
isInnerLoop = false;
}
}
if (!isInnerLoop) {
endOuterF(innerIndex, outerIndex);
outerIndex++;
}
} //end loop()
} //end stepThroughNestedLoop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment