Learning ColdFusion 9: Implicit Struct And Array Usage
Posted July 13, 2009 at 8:55 AM
ColdFusion first introduced implicit struct and array creation in ColdFusion 8. For those of use who were used to using JSON (Javascript Object Notation) in Javascript and AJAX data tranfers, implicit struct and array creation represented a nice, clean, efficient way to define complex data structures. ColdFusion 8 definitely had some limitations as far as this features went, but it looks like ColdFusion 9 has made some great improvements. While it might seem odd to kick off my "Learning ColdFusion 9" series with such a small feature, I think often times, it's the smallest features which create the most enjoyable coding experience over time.
For those of you who have not seen implicit structs and arrays before, it's a way to define structs and arrays using curly braces and square brackets (respectively) instead of StructNew() and ArrayNew() statements (respectively):
Launch code in new window » Download code as text file »
- <!---
- Creating an implicit array of implicit structs is basically
- the same in ColdFusion 8 and ColdFusion 9.
- --->
- <cfset girls = [
- {
- name = "Tricia",
- hair = "Brown"
- },
- {
- name = "Joanna",
- hair = "Dark Brown"
- }
- ] />
-
- <!--- Output the array. --->
- <cfdump
- var="#girls#"
- label="Girls"
- />
As you can see, rather than using ArrayNew(), we can simply use [ data ] to define an array and, instead of using StructNew(), we can simply use { key = value } to define a struct. When we run the above code, we get the following output:
| | | | | |
| | ![]() | | ||
| | | |
In ColdFusion 8, all we could do was use implicit struct and array creation to assign data structures directly to variables (as in the above demonstration). One of the most exciting new improvements made in ColdFusion 9 is that we can now use implicit struct and array creation directly in method calls and tag attributes without intermediary variables:
Launch code in new window » Download code as text file »
- <!---
- Using implicit values in methods applies both built-in
- ColdFusion functions as well as user defined methods.
- --->
- <cfset girl = {
- name = "Molly",
- hair = "Brown"
- } />
-
- <!--- Append new data to girl object. --->
- <cfset structAppend(
- girl,
- {
- smile = "Sweet",
- build = "Petite"
- }
- ) />
-
- <!--- Output the updated struct. --->
- <cfdump
- var="#girl#"
- label="Girl (via Method)"
- />
Notice that we are using the { .. } notation as one of the method arguments for StructAppend(). I am demonstrating a built-in ColdFusion function here, but this same technique works with user defined functions as well. When we run the above code, we get the following output:
| | | | | |
| | ![]() | | ||
| | | |
And, as I stated above, this notation can also be used directly inside of ColdFusion tag attributes:
Launch code in new window » Download code as text file »
- <!---
- Create the girl struct directly inside the tag
- attribute using the implicit struct notation.
- --->
- <cfdump
- var="#{ name = 'Libby', hair = 'Brown' }#"
- label="Girl (via Attribute)"
- />
Notice here that we are using the { .. } notation as one of the tag attribute values for CFDump. I am demonstrating a built-in ColdFusion tag here, but this same technique works with ColdFusion custom tags as well. When using this technique, be sure to wrap the implicit notation in hash tags (#) so that it gets evaluated as a statement. If you do not, it will just be passed through as a string. When we run the above code, we get the following output:
| | | | | |
| | ![]() | | ||
| | | |
Like I said, these ColdFusion 9 improvements might seem small, but they will have a huge payoff over the long run as our code becomes easier to write. To be honest, I think this is one of the most exciting features of ColdFusion 9. Of course, Adobe didn't just make implicit notation more robust, they also tried to fix some existing bugs. In ColdFusion 8, the following code would have thrown an error:
Launch code in new window » Download code as text file »
- <!--- Create an empty girls array. --->
- <cfset girls = [] />
-
- <!---
- Add a girl to the end of the array by directly accessing
- one past the end of the array.
- --->
- <cfset girls[ arrayLen( girls ) + 1 ] = {
- name = "Kathleen",
- hair = "Blonde"
- } />
-
- <!--- Output girls array. --->
- <cfdump
- var="#girls#"
- label="Girls"
- />
All this code is doing is adding a struct to the end of the array. But, due to the way ColdFusion 8 compiled implicit notation, it would have ended up referencing an undefined object. But like I said, this has now been fixed!
Several Order of Operations Bugs Still Exist
While these are some awesome improvements, it looks likes some implicit statement bugs still exist. Specifically, it looks like ColdFusion 9 is still evaluating the implicit statements in the wrong order. Technically, it should evaluate the right hand part of the statement first and then the left hand; but, it still looks like it is doing some weird, left-hand-first evaluation, which leads to unexpected errors.
In the following example, I'm creating an array with multiple elements. Then, I want to pair down the array, by reassigning the variable to be a new array containing the first element of the original array:
Launch code in new window » Download code as text file »
- <!--- Create a girls array with two entries. --->
- <cfset girls = [
- {
- name = "Tricia",
- hair = "Brown"
- },
- {
- name = "Joanna",
- hair = "Dark Brown"
- }
- ] />
-
- <!---
- Set girls array to be a new array containing the first
- element of the previous girls array assignment. In
- ColdFusion 8 this would throw an erro because the left
- side of stetement would be incorrectly evaludated first,
- rendering the right side no longer useful.
- --->
- <cfset girls = [ girls[ 1 ] ] />
-
- <!--- Output the new array. --->
- <cfdump
- var="#girls#"
- label="Girls (Reassigned)"
- />
Syntactically, this is completely valid as a statement; but, ColdFusion 9 still throws the following error:
The element at position 1 of dimension 1, of array variable "GIRLS," cannot be found.
The reason this is happening is because it is first reassigning the "girls" variable before it evaluates the right hand side of the statement. This is an incorrect order of operations. Technically, the new array should be created on the right hand side before the new girls variable is even touched.
If we take an existing simple value and try to turn it into a complex value using implicit struct creation, we get an even more interesting error. Take a look at this example:
Launch code in new window » Download code as text file »
- <!--- Start with a girl being just a name. --->
- <cfset girl = "Tricia" />
-
- <!---
- Now, take that name and turn it into a proper girl
- (note: not in the "make a woman out of her" way).
- --->
- <cfset girl = {
- name = girl,
- hair = "Brown"
- } />
-
- <!--- Output the newly formed girl struct. --->
- <cfdump
- var="#girl#"
- label="New Girl"
- top="5"
- />
Here, we are taking the girl variable, which holds a string value (name), and try to convert it into a struct in which the old string value becomes a new struct key value. Again, if the right hand part of the statement were evaluated first, this would be fine; but, due to an incorrect order of operations, we get this oddity:
| | | | | |
| | ![]() | | ||
| | | |
Because ColdFusion 9 is creating a new "girls" struct first, the "girls" reference within the new struct actually creates a circular reference, which is why our CFDump will go on infinitely.
This order of operations bug becomes even more evident (and ridiculous) when you can actually reference part of an implicit structure before you are even done defining it:
Launch code in new window » Download code as text file »
- <!---
- Looks like there is still a bug in the left/right order of
- evaluation for implicit data types. The following SHOULD
- throw a bug bcause "tricia.name" should not be availalbe at
- the time of the nickname setting.
- --->
- <cfset tricia = {
- name = "Tricia",
- hair = "Brown",
- nickname = ("Hottest " & tricia.name )
- } />
-
- <!--- Output our hottie. --->
- <cfdump
- var="#tricia#"
- label="Tricia"
- />
Notice here that as we are defining the "tricia" object, the last key actually references the first key in the same statement:
nickname = ("Hottest " & tricia.name )
Because "tricia" shouldn't technically exist yet at this point, this should throw an error; but, unfortunately, it does not. NOTE: This is a bug, not a feature, please please please do not try to leverage it.
ColdFusion 9 implicit struct and array creation has come a long way. Being able to use them directly in tag attributes and method calls (especially) is going to make programming significantly more enjoyable in some tasks. But, it looks like there is still a ways to go; while one bug was fixed, several ColdFusion 8 bugs are still very much alive.
Download Code Snippet ZIP File
Post Comment | Ask Ben | Other Searches | Print Page
Newer Post
Learning ColdFusion 9: Application-Specific Data Sources
Older Post
Adobe Launches ColdFusion 9 And Bolt Public Betas
Reader Comments
@Ben:
I think you're wrong about your deduction of:
"<cfset girls = [ girls[ 1 ] ] />"
I think the issue is girls[1] would point to a reference of the structure (since it's a complex value.) Since you're overriding the girls, you're corrupting the variable.
I suspect if you changed the line to:
<cfset girls = [ duplicate(girls[ 1 ]) ] />
Things would work as you expected.
@Dan,
The "girls" array and the structs inside of it exist independently. Therefore, girls[ 1 ] should simply point to "Tricia". Then, when I reassign the girls variable, it should become the new array:
[ TriciaObject ]
This works perfectly well in Javascript. It's because ColdFusion is improperly executing the order of operations in implicit statements.
@Ben:
I don't have easy access to CF9 at the moment, but knowing how CF works, I suspect using duplicate() will not throw the error. I suspect it'll also fix the issue in your next example.
I suspect CF's using references when setting implicit arrays/structures. Which is why you're getting the self referencing example with # <cfset girl = {name = girl, hair = "Brown"} />
A better test would be to just do:
// do not create a "girl" variable prior to this declaration
<cfset girl = {name=girl, hair="brown"} />
If the above line runs w/out "girl" being previously defined, then I'd suspect your conclusion is right.
@Dan,
Yes, the line:
<cfset girl = {name=girl, hair="brown"} />
... runs without error. It creates the infinitely nested CFDump.
I think the clearly the error is the way the statement is evaluated, not in the fact that objects are references. If that were the case, then this would cause errors in all languages that treat objects as refernces.
@Ben, I agree completely - the current behavior is horribly broken. The Adobe CF team needs to change the way assignment and evaluation are done, even if that breaks backward compatibility in certain edge cases (of people depending on CF's past broken implementation).
Another complaint I have is that CF changes the case of any struct keys to uppercase. If I want to store data, I don't want my data changed. CF should implement an additional syntax: #{ "key1" = value1, "key2" = [ { "key3" = value2 } ] }#. Any key enclosed in quotes (I am fine with no expression evaluation being allowed for key names in quotes) should *always* preserve the case of the key as given. In addition, any text should be permitted as a key, so long as it is provided in quotes - bringing CF object/array notation on par with JavaScript's.
@Justice,
I'd say it's buggy, but not horribly broken. I'd love if they fixed it, naturally; however, I think the improvements that they have made so far are going to be very helpful.
@Ben:
That's definitely interesting and definitely broken. Thanks for the info!
@Justice - at our CFUG tour event, I asked Adam about the uppercasing of struct keys when using the implicit structure creation (and how that makes it useless for flex or javascript) and it sounded like a) he knows about the issue and b) they are working on it. I am hoping that makes it into a later build.
This isn't really about cf9, but the implicit struct creation.
I've avoided implicit struct use because of way that cf makes the keys uppercase (same as when use dot notation as creation method prior to ability to do implicit). Often I'm sending a CF structure (or array of cf structs) to something like actionScript and case becomes important. Therefore, I usually use dot notation in creating my keys. (Has been easier to do this to fit with other languages rather than make other languages fit CF.)
Unless I'm missing something, I haven't found a way to use implicit struct creation and have case sensitive keys.
Is there a way to make the case-sensitive keys while doing implicit struct creation? Would save some steps to use implicit but the key case is a hurdle...for me, at least.
Keith
@Keith,
From what it sounds like, there is no way to keep keys in-case with implicit creation as you cannot currently quote keys.
@Keith Dodd,
I use the following functions (I don't recall where I found them) for object/array "literals:"
These typically allow for better semantics, including case-sensitivity.
function $s() { var s = { }; StructAppend(s, Arguments, true); return s; }
function $a() { var a = [ ]; var i = 0; for(i = 1; i <= ArrayLen(Arguments); i += 1) ArrayAppend(a, Arguments[i]); return a; }
<cfset x = $s(key1: value1, key2: $a(3, 4, 5)) />
Definitely both funky bugs. Just to clarify too, I think we should state that these relate to the ColdFusion 9 *Public Beta* - hopefully they won't be present in the final release of ColdFusion 9 ;)
@Justin,
Good point. This is our time to find all the bugs :)
Have any of these issues been logged in the public bug tracker? If not please do so and report the issue number(s) so we can vote. We have a public bug tracker, let's use it!
There also seems to a general issue with ColdFusion's evaluation logic. Take the following example:
users[];
for (i=1;i<10;i++) {
users[i] = user = {};
}
This throws a "user undefined" error in the loop.
Did any one try using quoted strings with implicit struct creation ? Hopefully that should make more people like it :)
Oh and btw you should also try giving keys as expression. An example would be:
<cfset hair="hair color" >
<cfset tricia = {
name = "Tricia",
"#hair#" = "Brown",
"nickname" = ("Hottest " & tricia.name )
} />
<!--- Output our hottie. --->
<cfdump
var="#tricia#"
label="Tricia"
/>
p.s. CF9 only
Ohh, forgot to add that a quoted strings would retain its case.
@chandan,
Unfortunately, quoted strings on the implicit struct keys is not supported.
@Nathan,
I also get that error when trying the code. Definitely a multiple assignment bug.








