Assigning Variables Within A CFLoop Condition In ColdFusion

Posted August 4, 2009 at 1:59 PM

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:

 Launch code in new window » Download code as text file »

  • <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:

 Launch code in new window » Download code as text file »

  • <!--- 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:

 Launch code in new window » Download code as text file »

  • <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:

 Launch code in new window » Download code as text file »

  • <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.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Print Page




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 »
6,516 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 »
23 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 »
6,516 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 »
18 Comments

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


Sep 24, 2009 at 1:24 PM // reply »
26 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 »
26 Comments

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


Sep 24, 2009 at 1:53 PM // reply »
6,516 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 »
26 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 »
6,516 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 Comment  |  Ask Ben

Recent Blog Comments
Nov 21, 2009 at 6:47 PM
Hal Helms - Real World Object Oriented Development, Sarasota - Day Five
@charlie griefer, Thank you.. ... read »
Nov 21, 2009 at 5:15 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jose Galdamez, Oh heh yeah I didn't paste the whole code. I should have defined the vars -- my bad. It's fixed thou. Thanks. ... read »
Nov 21, 2009 at 4:49 PM
Styling The ColdFusion 8 WriteToBrowser CFImage Output
Great work yet again Ben! Whilst I didn't use this whole code, I copied some of your regex code for a similar problem with the lack of an alt attribute and unescaped ampersands in CFIMAGE for Railo 3 ... read »
Nov 21, 2009 at 1:13 PM
My First ColdFusion Builder Extension - Encrypting And Decrypting CFM / CFC Files
@Ben, Because I am pedantic, I just want to make sure that everyone knows there is absolutely no encryption going on. There is only encoding and obfuscation. The cfencode tool only obfuscates your C ... read »
Nov 21, 2009 at 12:28 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jody I can't seem to get your code sample to work. If you are still having problems, try this code out and see if it gets you what you wanted. <!--- Comma delimited list with various duplicates ... read »
Nov 21, 2009 at 11:03 AM
Groovy Operator Overloading Does Not Work In The ColdFusion Context
Hi Ben, Thanks for this informative post. Now I am reading ur old posts too ... read »
Nov 21, 2009 at 10:56 AM
HostMySite.com Has The Best ColdFusion Hosting
@Mehul, Yes very nice people, however several downtimes per day which was not acceptable. Hence we had to move out. I am glad you are having good luck with them so far. ... read »