Under the Hood - ColdFusion Array
Posted on November 12, 2007
It’s very easy to iterate (loop) over a simple array variable. It’s also trivial to get a specific element. But when the array is returned from a function, you can’t just ask for getFoo()[x]. Or can you?
A ColdFusion Array
Let’s start out with a simple variable of type array.
<cfset foo = arrayNew(1) />
<cfset foo[1] = "A" />
<cfset foo[2] = "B" />
<cfset foo[3] = "C" />
<cfset foo[4] = "D" />
<cfset foo[5] = "E" />
<cfdump var="#foo#" />
output
array | |
---|---|
1 | A |
2 | B |
3 | C |
4 | D |
5 | E |
Getting any element of the array is simple:
<cfouput>#foo[3]#</cfouput>
outputC
Function of returntype Array
Now let’s create a function that returns the array:
<cffunction name="getFoo" returntype="array">
<cfreturn foo />
</cffunction>
<cfdump var="#getFoo()#" />
output
array | |
---|---|
1 | A |
2 | B |
3 | C |
4 | D |
5 | E |
No difference, right? Wrong. We can’t retrieve a particular element of the array in the same manner as the simple variable.
Error
<cfouput>#getFoo()[3]#</cfouput>
Normally we’d just assign the value of the function to another variable and run the code as usual.
Passing the buck
<cfset wibble = getFoo() />
<cfoutput>#wibble[3]#</cfoutput>
outputC
Calling on the underlying Java
Many of us (CF developers) tend to forget that there’s Java under all that CFML. I remembered someone else had blogged about being able to callJava String functions directly on ColdFusion String variables within <cfoutput>
<cfset y = "hello" />
<cfoutput>
#y.toUpperCase()#
</cfoutput>
outputHELLO
So it would stand to reason that a ColdFusion array is converted to some Java object that can contain multiple elements. And if it’s a Java object, then there has to be some way to access each element.
Something like a getter
in a bean.
Some way to get
each item in the object.
<cfoutput>
#getFoo().get(3)#
</cfoutput>
outputD
Iterating over the returned array
It’s easy to loop (iterate) over the simple array variable:
<cfloop index="x" from="1" to="#arrayLen(foo)#">
#x#: #foo[x]#<br />
</cfloop>
output
1: A
2: B
3: C
4: D
5: E
Now we can use the same syntax directly with the function without having to create another variable to reference the function’s value.
But did you notice something different between the two outputs?
foo[3]
outputC
getFoo().get(3)
outputD
The reason for this is that ColdFusion begins indexing elements at position 1
, while Java begins at 0
.
So we’ll have to take that into consideration when looping over the array.
<cfoutput>
<cfloop index="x" from="1" to="#arrayLen(getFoo())#">
#x#: #getFoo().get( x-1 )#<br />
</cfloop>
</cfouput>
Error
The selected method get was not found.
So there’s one last hurdle to clear here. The get()
method is a Java method and we’re trying to pass a ColdFusion variable to it. Java doesn’t know what to do with the CF variable, so it throws an error.
Since we know we can pass a numeric value to the function get()
, we just have to make sure that CF passes the actual value of x
to Java.
<cfoutput>
<cfloop index="x" from="1" to="#arrayLen(getFoo())#">
#x#: #getFoo().get( evaluate( x-1 ) )#<br />
</cfloop>
</cfoutput>
output
1: A
2: B
3: C
4: D
5: E
Update per the Comments
I guess I was still in a CF mindset when trying to pass x
to get()
. I should be using JavaCast() to tell Java what kind of value get()
would be receiving.
<cfoutput>
<cfloop index="x" from="1" to="#arrayLen(getFoo())#">
#x#: #getFoo().get( javaCast("int", x-1 ) )#<br />
</cfloop>
</cfoutput>
Thanks Mark, Sean and Ben.
Under the Hood
I Google’d around for a minute trying to find out which Java object was under the ColdFusion array and found a post by Christian Cantrell from 2003. According to him, a ColdFusion array
is actually a Java Vector
. So that means that we can call any Vector method on a ColdFusion array.
Now, instead of using a ColdFusion array function:
<cfloop index="x" from="1" to="#arrayLen(getFoo())#">
I can call a Vector function on the value returned from the ColdFusion function:
<cfloop index="x" from="1" to="#getFoo().size()#">
or I can call the Vector function directly on the original variable:
<cfloop index="x" from="1" to="#foo.size()#">
So why would I want to do this?
Really, I have no idea. I was working on another post for the OO CF Primer and started thinking too much when I got to a part that talks about retuning an array from a Bean via a getter.
I know that you would NOT want to use this syntax when calling a function that actually runs some data logic or database access. Especially if you’re looping over the array of results. That would cause you to run the function’s logic or database calls for each iteration of the loop.
However, if the function is just returning the value of a variable that was set by some other process, like a property of a Bean, then you could use this syntax to avoid creating another variable or to keep with the a.b.this.that syntax of the OOP world.
But at least if you didn’t know before, now you know that a ColdFusion array is a Java Vector.
And knowing is half the battle.
*flees*
Adrian J. Moreno
Adrian is a CTO and solution architect specializing in software modernization. More information