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  |  Print Page


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 »
7,572 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 »
7,572 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 »
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 »
7,572 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 »
7,572 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
Mar 20, 2010 at 12:07 PM
Drawing On The iPhone Canvas With jQuery And ColdFusion
Simply awesome. Saved my day. ... read »
Mar 20, 2010 at 9:00 AM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
I would like to say thx for an easy way to create a bottom bar. I do have a ?. Is it possible to center the bar if i want to resize it to ex 85%. Regards Offenbach ... read »
Mar 19, 2010 at 7:26 PM
MySQL 3/4 - com.mysql.jdbc.Driver And allowMultiQueries=true
Thank you very much for this post. Adding allowMultiQueries="true" in context.xml didn't help until I added it to url as allowMultiQueries=true Good idea is to use prepared statements and it will he ... read »
Jim
Mar 19, 2010 at 4:49 PM
Nobody Puts Baby In The Corner!
Wow. This is like suddenly finding a support group for your secret shame. I'm not alone! I always liked this movie, even though it is extremely cheesy. I just wish Jennifer Grey hadn't gotten the ... read »
Mar 19, 2010 at 4:47 PM
Application.cfc OnRequest() Method Affects OnError() Arguments
@Jason and @Ben, I've been doing some CF9 refactoring on our systems and noticed an odd occurrence with onError as well. Found a way to work around my problem, but what I saw was... Background: Our ... read »
Jim
Mar 19, 2010 at 4:44 PM
Shoot 'Em Up Starring Clive Owen And Paul Giamatti
I actually enjoyed this movie quite a lot. It was different, certainly, but I think they were going for more of a Quentin Tarentino-"wow, that was weird"-vibe than an actual spoof. Once I realize ... read »
Mar 19, 2010 at 4:34 PM
An Intensive Exploration Of jQuery With Ben Nadel (Video Presentation)
Hey I guess the video is down. Is there anyway you can upload to youtube or vimeo or some other service? Greatly appreciated. ... read »
Mar 19, 2010 at 4:24 PM
ColdFusion CFPOP - My First Look
@Ben Thanks for the follow up! The root of the problem had to do with being able to trace bounced emails to specific records in a DB table. Let's say you run an email campaign and you get 1,000 bou ... read »