Assigning Variables Within A CFLoop Condition In ColdFusion

Posted August 4, 2009 at 1:59 PM by Ben Nadel

Tags: ColdFusion

This morning, when I was exploring ColdFusion 9's new ArrayDelete() function, I actually stumbled upon a feature in ColdFusion that I had never tried before. This is something that I often do in Javascript, which is the only reason I actually tried it this morning without thinking. What I'm talking about is assigning a value to a variable from within the Condition of a CFLoop tag.

Often times, the Condition attribute of a CFLoop tag simply checks to see if a condition is true or false. Something like this:

  • <cfloop condition="(arrayLen( someData ) GT 5)"> .... </cfloop>

Here, our CFLoop tag will continue looping while the array, someData, has a length greater than five.

What I discovered this morning, however, was that if the Condition attribute contains a variable assignment, not only will the variable be assigned successfully, it will then also be further evaluated as a boolean:

  • <!--- Create a list of girls. --->
  • <cfset girls = "Sarah,Molly,Sarah,Joanna,Sarah" />
  •  
  • <!---
  • Keep looping over the girls and deleting the name "Sarah"
  • from the list. In the Condition attribute we are going to
  • BOTH assign a variable and test it's boolean value.
  • --->
  • <cfloop condition="targetIndex = listFind( girls, 'Sarah' )">
  •  
  • <!---
  • Delete instance of Sarah using the stored index value
  • from our condition. Note: this is not a peronal thing -
  • Sarah is a really aweosme girl.
  • --->
  • <cfset girls = listDeleteAt( girls, targetIndex ) />
  •  
  • <!--- Output the notification. --->
  • Delete Sarah at: #targetIndex#<br />
  •  
  • </cfloop>

Here, the Condition of the CFLoop tag gets the list index of a given value and assigns it to the variable, targetIndex. TargetIndex is, itself, then evaluated as a boolean condition, determining whether or not the CFloop should continue executing. And, running the above code, we get the following output:

Delete Sarah at: 1
Delete Sarah at: 2
Delete Sarah at: 3

Sweet-ass-sweet, it works like a charm!

Ironically, the same functionality is not available in a WHILE loop within CFScript. The attempt to recreate this in CFScript:

  • <cfscript>
  •  
  • // Assign girls list.
  • girls = "Sarah,Molly,Sarah,Joanna,Sarah";
  •  
  • // Keep looping while Sarah can be found.
  • while (targetIndex = listFind( girls, "Sarah" )){
  •  
  • // Delete target element.
  • girls = listDeleteAt( girls, targetIndex );
  •  
  • // Output results.
  • writeOutput( "Delete Sarah at: #targetIndex#<br />" );
  •  
  • }
  •  
  • </cfscript>

... throws the following ColdFusion compile time error:

ColdFusion was looking at the following text: = The CFML compiler was processing: A script statement beginning with while on line 41, column 9.

If you want to, you can sort of hack the CFScript-based FOR loop to act this way, but it just looks junky:

  • <cfscript>
  •  
  • // Assign girls list.
  • girls = "Sarah,Molly,Sarah,Joanna,Sarah";
  •  
  • // Keep looping while Sarah can be found.
  • for (
  • targetIndex = listFind( girls, "Sarah" ) ;
  • listFind( girls, "Sarah" ) ;
  • targetIndex = listFind( girls, "Sarah" )
  • ){
  •  
  • ...
  •  
  • }
  •  
  • </cfscript>

So, I guess the only thing appropriate to say at this point is, In your face CFScript!! Ok, ok, all joking and feelings of tag-superiority aside, I am quite happy to have found this feature available in conditional CFLoop tags.


You Might Also Be Interested In:



Reader Comments

Aug 4, 2009 at 2:11 PM // reply »
5 Comments

It may be just a syntax over sight. Replacing = with EQ fixes the error. However, the code using EQ will still throw an error.

"Variable TARGETINDEX is undefined."


Aug 4, 2009 at 2:13 PM // reply »
10,640 Comments

@Steve,

Right, because EQ turns it from an assignment into a comparison. That will work, but because there is no assignment, the targetIndex is no longer valid.


Aug 4, 2009 at 2:15 PM // reply »
25 Comments

Apparently Adobe dropped the ball with "everything you can do with tags you can do with script", eh? Of course, I'd probably "fix" that code by adding the second equals sign (to make an equality instead of an assignment) if I saw it.

Here's a better way with CFSCRIPT:

while (true) {
targetIndex = listFind(girls, "Sarah");
if (targetIndex EQ 0) {
break;
}
girls = listDeleteAt(girls, targetIndex);
}


Aug 4, 2009 at 2:15 PM // reply »
5 Comments

@Ben

Ahh, I gotcha. I really haven't used the condition attribute much, maybe only a handful of times. Good to know though!


Aug 4, 2009 at 2:24 PM // reply »
10,640 Comments

@Barney,

Yeah, that is definitely a better way. I was just trying to translate the variable-assignment-as-part-of-operator functionality.

The biggest discrepancy that I'm seeing in CFScript is CFHeader and CFContent. I cannot find any information on these.

@Steve,

Yeah, condition is the most popular loop feature. Heck, have the time I use it, my condition is "True" as in Barney's example, although mine is tag-based.


Aug 11, 2009 at 5:58 PM // reply »
19 Comments

I like this, it feels gooood. Will def be useful. Thanks Ben!


Sep 24, 2009 at 1:24 PM // reply »
29 Comments

Did some other tests.

Type 0

<cfloop condition="(x = 0)"></cfloop>

This doesn't work. Error: "Invalid CFML construct found on line 1 at column 5. ColdFusion was looking at the following text: ="

This seems different than normal expressions which use parentheses as precedence.

Type 1

<cfloop condition="x = 0"></cfloop>

This works, but maybe not as one might expect. The loop never runs. An assignment loop takes the value of the variable as the condition value.

This is in common with C, C++, and Perl. But not Python or Ruby.

No biggie. Until you see type 3.

Type 2

<cfloop condition="x = reFind(p, s)"></cfloop>

This works great! Thanks CF team.

Type 3

<cfloop condition="x = reFind(p, s, true)"></cfloop>

This doesn't work. Error: "Cannot convert the value of type class coldfusion.runtime.Struct to a boolean".

Similar to type 1. IMHO, Shouldn't it be anything but 0/false/undefined evaluates as if it were true?

This commonly works in other languages.

Another strike against the CF expression evaluator.

Type 4

<cfloop condition="((x = 1) and (x gt 0))"></cfloop

This doesn't work. Error: "Invalid CFML construct found on line 1 at column 5. ColdFusion was looking at the following text: ="

Similar to type 0.

<cfloop condition="x = 1 and x gt 0">

This doesn't work. Error. "Variable X is undefined."

Shouldn't x have been set by the first clause?

This commonly works in other languages. It's called precedence.

Another strike against the CF expression evaluator.

Type 5

<cfloop condition="((structKeyExists(url, ""x"")) and (x gt 0))"></cfloop>

This works great! Thanks CF team.

But how come precedence works for this type of expression, but not for assignment?

Thoughts?


Sep 24, 2009 at 1:26 PM // reply »
29 Comments

Shoot. Forgot to label Type 5. Type 6 is incorrectly labelled Type 5.


Sep 24, 2009 at 1:53 PM // reply »
10,640 Comments

@Alex,

I think what it comes down to is that the Condition value is evaluated as a ColdFusion expression (perhaps using the Evaluate() method or something similar). When it does that, it runs the code and then, I guess, uses the result as a Boolean.

Some of your Conditions throw errors because they are not valid ColdFusion statements. If you tried to run:

<cfscript> (x=0); </cfscript>

... as a standard CF statement, it would throw the same error - not valid syntax.

Also, because the condition needs to be evaluated as a Boolean, you have to get a full understanding of what is a "Truthy" value in ColdFusion, which is limited to non-zero numeric values (or things that can be implicitly converted to numbers), true keywords, and YES. Anything else probably cannot be converted to a boolean, which is why the reFind() that returns an array fails.


Sep 24, 2009 at 2:13 PM // reply »
29 Comments

@Ben,

> (x=0)
> not valid syntax

I understand.

Then expression syntax is different for assignment expressions than it is for comparison expressions.

Because these work fine:

assignment: <cfloop condition="x = 0"></cfloop>
comparison: <cfloop condition="(x eq 0)"></cfloop>
comparison: <cfloop condition="((x eq 0) or (x eq 1))"></cfloop>


Sep 24, 2009 at 2:17 PM // reply »
10,640 Comments

@Alex,

Yeah, exactly - some small differences. I actually prefer to use parenthesis around my conditions, so it's a shame that it throws an error :(


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »
Feb 9, 2012 at 10:29 PM
Learning ColdFusion 9: Application-Specific Data Sources
@Ben, No offence, but if people were really wanting advanced features they would be using a platform like ASP.NET MVC. CFML is so structurally compromised as a tag-based scripting language that ... read »
Feb 9, 2012 at 10:03 PM
Subversion - Cleanup Failed To Process The Following Paths
@Leviaguirre, do you still have problems with this? ... read »