ColdFusion 10 - Looping Over Function Arguments
Since ColdFusion 9.0.1, CFScript has supported using a FOR-IN loop construct for both arrays and structures. In arrays, it loops over the indices; in structures, it loops over the keys. Typically, this is a clear-cut piece of functionality. But, when it comes to the Arguments collection, which exhibits both Array and Struct behavior, the FOR-IN loop can be a bit confusing. This duality tripped me up the other day. While ColdFusion offers an existing way around this (ie. using a standard FOR-LOOP), I thought it would be nice to briefly explore Arguments-looping in ColdFusion 10.
NOTE: At the time of this writing, ColdFusion 10 was in public beta.
While the Arguments collection exhibits both Array-like and Struct-like behavior, if you try to iterate over it using a FOR-IN loop, you'll quickly discover that it uses struct-iteration, not array-iteration. To force array-iteration, you could fallback to using an explicit FOR-LOOP; or, you could use one of ColdFusion 10's new "functional programming" methods for enhanced clarity.
In the following demo code, I'm simply going to demonstrate the various types of Arguments-iteration that we now have available in ColdFusion 10:
<cfscript>
// I simply provide a harness for exploring the relationship
// between the Arguments scope and script-based iteration.
function doSomething( argA, argB ){
// First, try the FOR-IN loop (ColdFusion 9.0.1).
writeOutput( "FOR-IN Loop <br />" );
for (var i in arguments){
writeOutput( "[#i#]: " );
writeOutput( arguments[ i ] & "<br />" );
}
// Now, try the structEach() loop (ColdFusion 10).
writeOutput( "<br />" );
writeOutput( "structEach() Loop <br />" );
structEach(
arguments,
function( key, value ){
writeOutput( "[#key#]: " );
writeOutput( value & "<br />" );
}
);
// Now, try the arrayEach() loop (ColdFusion 10).
writeOutput( "<br />" );
writeOutput( "arrayEach() Loop <br />" );
arrayEach(
arguments,
function( value ){
writeOutput( value & "<br />" );
}
);
// Now, try the FOR-I loop.
writeOutput( "<br />" );
writeOutput( "FOR-i Loop <br />" );
for (var i = 1 ; i <= arrayLen( arguments ) ; i++){
writeOutput( "[#i#]: " );
writeOutput( arguments[ i ] & "<br />" );
}
}
// ------------------------------------------------------ //
// ------------------------------------------------------ //
// Try the script - notice that there are MORE parameters in this
// invocation than there are arguments defined in the function
// signature.
doSomething( "Our", "Deepest", "Fear" );
</cfscript>
As you can see, we're using the following approaches:
- FOR-IN
- structEach() - New in ColdFusion 10
- arrayEach() - New in ColdFusion 10
- FOR-I
When we run the above code, we get the following output:
FOR-IN Loop
[3]: Fear
[ARGB]: Deepest
[ARGA]: OurstructEach() Loop
[ARGA]: Our
[3]: Fear
[ARGB]: DeepestarrayEach() Loop
Our
Deepest
FearFOR-i Loop
[1]: Our
[2]: Deepest
[3]: Fear
This is a really minor post, I know; but, the dual-nature of the Arguments scope planted a little bug in an application I was writing the other day, so I thought it was worth sharing.
Want to use code from this post? Check out the license.
Reader Comments
This might be a bit off topic, but while I like the functionality of arrayEach and structEach, I would have preferred a more conventional syntax. For example:
If x is a struct:
x.each(function(key,value){
//Do stuff
});
If x is an array:
x.each(function(value){
//Do stuff
});
@Scott,
I think that sentiment is one commonly held in the ColdFusion world. I have been in meetings with Adobe where that is discussed and I think the feeling is that it would just be too big a departure for the current syntax and would basically kill backwards compatibility. But, I agree, that would be awesome.
Huh, I didn't know there was a for-in structure in CF yet... *rubs hands evily*
time to revisit some code!
@Ben:
I labored hard when they introduced the Image* functions that they be implemented as objects and not dozens of function calls.
They just keep polluting the function name space and I think that also tends to promote procedural code in some way. I've also wondered if they start using a more OO approach, if maybe people the CF haters would somehow see it as a more legitimate language.
And I've never understood the "backwards compatible" argument about the approach. If you're adding new functionality, then by it's nature it's not backwards compatible. (Granted, you could add functions that emulate compatibility, but that's a different issue.)
@Jim,
Yeah, the For-In construct is awesome! I've been on ColdFusion 8 for so long, I forgot that it had been added in ColdFusion 9. That's the problem with not upgrading - you forget about the cool new features :(
@Dan,
I agree - if ColdFusion took on a more object-oriented approach, I think it would get a better reputation. Plus, I think it would be possible to build "wrapper" functions that just invoke the object methods in turn in order to create backwards compatibility. But, as you say, when you add new functionality, it seems that backwards compatibility becomes a moot point.
@ Ben
@Dan
In CF9 I believe they incorporated ORM (Object Relational Mapping). I haven't explored it yet but just thought to offer some input. :) Hope it helps. There is a video on it in the CF9 DevNet.
http://www.adobe.com/devnet/coldfusion/articles/cf9_cfbuilder_videos.html
(It's the first vid by Ben Forta; yes, another ubber Ben in the world of CF!)