Posted in ColdFusion | Posted on 05-09-2007 | 2,584 views
Following up on my series of blog entries from last week, here is another gotcha you want to look out for when using ColdFusion arrays. When defining an array in ColdFusion, you typically set them starting with 1 and ending with some number:
2<cfloop index="x" from="1" to="10">
3 <cfset arr[x] = randRange(1,100)>
4</cfloop>
This creates an array with items in indexes 1 to 100. But - consider this example:
2<cfloop index="x" from="1" to="10">
3 <cfset arr[randRange(1,100)] = x>
4</cfloop>
Now this is a bit convoluted, but this code example assigns values based on a random index. It will create 10 array values in indexes from 1 to 100 (and it may even do the same index twice).
What happens if you try to loop over this array? The normal way to loop over an array is to use arrayLen:
If you run this code on the array we just created, you will get an error because the indexes aren't all defined. What about isDefined()? It won't work on arrays. So what do you do?
One solution is to try/catch:
2 <cftry>
3 <cfoutput>Item #x# is #data[x]#<br /></cfoutput>
4 <cfcatch></cfcatch>
5 </cftry>
6</cfloop>
Another option is to use a UDF that wraps up the same logic. You can find arrayDefinedAt() at CFLib. Here is an example of code that uses it:
2 <cfif arrayDefinedAt(data,x)>
3 <cfoutput>Item #x# is #data[x]#<br /></cfoutput>
4 </cfif>
5</cfloop>
Lastly, you can just wait for Scorpio! One of the announced features is an arrayiIsDefined function. Ok, most likely you can't wait for Scorpio, but it's nice to know Adobe took care of this problem.


Something along the lines of...
<cfloop index="x" from="data.first()" to="#data.end()#" step="#data.next()#">
...
</cfloop>
OR
<cfloop index="x" array="#data#">
...
</cfloop>
Am I just thinking wishfully here?
Here's an example I've run into: An array of error codes and their text equivalents:
arrErrors[10] = "Unexpected Result from the database";
arrErrors[13] = "Improperly formed parameter";
arrErrors[14] = "Action not allowed";
Say you wanted error #10 to be "Unexpected Result from the database" across your entire app, but errors 1-9 aren't applicable to the object you're coding. So you call an error handler with setError(10), which looks up the right error in the arrErrors array.
You could code it as a struct, but an array is a faster lookup.
The iterator works a treat, cheers for the pointers.
<cfset test = arrayNew(1)/>
<cfset test[1] = "one"/>
<cfset test[3] = "three"/>
<cfset test[5] = "five"/>
<cfset testIterator = test.iterator()/>
<cfloop condition="testIterator.hasNext() eq true">
<cfoutput>#testIterator.next()#</cfoutput>
</cfloop>
<cfquery name="Get_RFP" datasource="#application.DSN#" >
SELECT tblRPEmp.RespID, tblRP.ID, tblRP.CtrlNum
FROM tblRP Etc Etc
</cfquery>
<!--- See if there are more than one RespID / CtrlNum--->
<cfset tblRP_CtrlNum_Valuelist = ValueList(Get_RFP.CtrlNum) >
<!--- Find out who is the Responsible partner --->
<cfoutput query="Get_RFP">
<cfset "RspNames_#CtrlNum#_#RespID#" = #LName#>
</cfoutput>
<!--- See how many CtrlNum's there are --->
<cfset TMPListValueCount = ListValueCount(tblRP_CtrlNum_Valuelist, CtrlNum)>
<!--- if more than one --->
<cfif TMPListValueCount NEQ 1>
<!--- See if they exist and then Spit out who the Responsible partners --->
<cfLoop Index="L" FRom="2" To="5">
<cfif IsDefined("RspNames_#CtrlNum#_#L#")> #Evaluate("RspNames_#CtrlNum#_#L#")# - #L# <br /> </CFIF>
</CFLOOP>
</CFIF>
http://rickosborne.org/blog/index.php/2007/05/10/s...
This syntax works as well.
<cfset elements = arr.elements() />
<cfloop condition="#elements.hasMoreElements()#">
#elements.nextElement()#<br />
</cfloop>
Keep in mind that both of these return empty strings for undefined array elements.
@jason - that seems a little more elegant, might go and revisit my code again, cheers :)
[Add Comment] [Subscribe to Comments]