Using Labeled Loops In JavaScript
Earlier this week, I looked at using labeled loops in ColdFusion. Labeled loops allow you to break
and continue
an outer loop from within the context of an inner loop by explicitly naming your loop statements. I had never used this functionality in ColdFusion before; and, it turns out, the same functionality is available in JavaScript. As such, I wanted to try it out in the browser as well.
Run this demo in my JavaScript Demos project on GitHub.
View this code in my JavaScript Demos project on GitHub.
Modern ColdFusion looks more-or-less like modern JavaScript, especially when you take-out the type-checking (just another reason why ColdFusion is such an amazing language). In fact, I can pretty-much copy the "Fuzzy Matching" demo from my previous post and paste it in a JavaScript runtime with only slight modification.
In the following exploration, we're going to try and "fuzzy match" search text against a given target text. To do this, we're going to loop over the search text and - for each character in the search text - loop over the target text characters, trying to find a sequential match. This gives us the opportunity to explore a nested loop context.
ASIDE: This algorithm can be refactored to work without labeled loops, as Emre Yucel pointed out in my previous post. But, the point of this demo is to look at labeled loops. So, thank you for just going with the flow.
When a matching character is found within the inner loop, we will either continue
or break
out of the outer loop - using the outer loop label - based on the current condition:
<!doctype html>
<html lang="en">
<body>
<h1>
Using Labeled Loops In JavaScript
</h1>
<script type="text/javascript">
// Fuzzy matches.
console.log( isFuzzyMatch( "horse", "s" ) );
console.log( isFuzzyMatch( "horse", "hs" ) );
console.log( isFuzzyMatch( "horse", "horse" ) );
// No matches.
console.log( isFuzzyMatch( "horse", "horses" ) );
console.log( isFuzzyMatch( "horse", "test" ) );
console.log( isFuzzyMatch( "horse", "" ) );
// --------------------------------------------------------------------------- //
// --------------------------------------------------------------------------- //
/**
* I determine if the given target text is a fuzzy-match for the given search text.
*/
function isFuzzyMatch( targetValue, searchValue ) {
var searchChars = [ ...searchValue ];
var targetChars = [ ...targetValue ];
var matchFound = false;
var s, t;
searchLoop:
while ( s = searchChars.shift() ) {
targetLoop:
while ( t = targetChars.shift() ) {
// We found a matching CHARACTER in the target string.
if ( s == t ) {
if ( ! searchChars.length ) {
matchFound = true;
// If we've run out of search-characters to consume, it means
// that the entirety of the search keyword was located (in
// parts) within the target text. In other words, we HAVE a
// fuzzy-match. Yay! At this point, there is nothing left to
// search and we can break out of BOTH the INNER and OUTER
// loops.
break searchLoop;
}
// If we have more search characters to consume, move onto the
// NEXT ITERATION of the OUTER loop and the next search character.
continue searchLoop;
}
}
// If we've fully consumed the target characters, there's no sense in
// continuing to consume the search characters - we will not find a match.
break;
}
return( matchFound );
}
</script>
</body>
</html>
As you can see, each of the while
-loops above is preceded by a label:
statement. The outer loop is labeled as searchLoop:
, which allows my inner loop to use break searchLoop
and continue searchLoop
statements. These will break out of or continue onto the next iteration of the outer loop, respectively.
FORMATTING NOTE: You can, technically, include the loop label on the same line as the loop itself. However, this should be avoided as it looks too much like Object-key assignment; and, is more likely to cause confusion.
Now, if we run this page in the browser we get the following console output:
true // isFuzzyMatch( "horse", "s" )
true // isFuzzyMatch( "horse", "hs" )
true // isFuzzyMatch( "horse", "horse" )
false // isFuzzyMatch( "horse", "horses" )
false // isFuzzyMatch( "horse", "test" )
false // isFuzzyMatch( "horse", "" )
As you can see, by using labeled loops, we were able to exert control flow on the outer loop from within the context of the inner loop. My demo uses while
-loops but this also works for for
-loops. In fact, you can even break out of a block - but that's just plain bananas!
Want to use code from this post? Check out the license.
Reader Comments
This is possible in CFML, but only in Lucee so far as I know. Rory covers it here:
https://rorylaitila.gitbooks.io/lucee/content/control_flow.html#for-loop-with-label
@Brad,
It seems to work in ACF 2021 as well.
That's awesome news. I did a quick Google prior to posting and no docs from either Adobe nor Lucee came up. I've also heard Lucee present it, but not Adobe. Seems both of them need to beef up their docs!
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →