Learning ColdFusion 9: Implicit Struct And Array Usage

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

<!---

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:

<!---

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:

<!---

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:

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

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

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

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.

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

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.

This came up as the #1 Google result for "struct key list case sensitive cold fusion 9", so I thought it might be useful to note that as of ColdFusion 9.0.1, you can provide an implicit struct with quoted keys that will retain the case of their alpha characters.

Just wanted to put this out there for anyone wanting to keep the alpha case of string struct keys intact and use implicit structs or if they wanted to know if you can use quoted struct keys when defining implicit structs in CF9.

(Hopefully this doesn't violate the "no large chunks of code" comment etiquette directive.)

This is definitely a nice upgrade in ColdFusion 9. I actually just saw this (quoted keys) for the first time at this past CFUNITED at Elliott Sprehn's presentation. I actually just blogged about it a few days ago:

Haha, I just saw that you covered it already! I was using some CF and structs to store and render some jQuery dropdown menus at CF runtime, keeping my menus for each module of the app as unordered lists of anchors (links) in html files, then sorting them into a {AppRoot}/{AppName}/UI/Menu/{UserType}/{ModuleName}.html file layout.

So when a user logs in, it has a predefined struct with "ModuleFileName" = "Human Readable Module Name" key/value pairs, and then I use cfsavecontent and cfloop to run through the struct and store the appropriate html elements in a menuButtons variable and the appropriate JavaScript in a menuJavaScript variable, store them in the session scope and simply cfoutput them to the appropriate spots in the page layout.

Having all upper case file names was causing me a headache (cause I make an AJAX call to get the menu contents and it was being picky over the case of the filename) so being able to keep my keys' case was just the thing I needed. :)

Are you on a UNIX-type system? I assume that's why you needed to keep the file names uppercase, right? In any case (no pun intended), it's just nice to have casing appropriate. I was fooling around with a bunch of AJAX over the weekend and had to kept creating the structs before I assumed keys (using CF8). I'm soooo ready for this update!

I actually ran into an error today creating an implicit variable of monstrous size. (The one assignment expression, allowing for readability, spans 280 lines.)

For whatever reason, CF9 actually wouldn't allow me to execute the implicit array/struct assignment without quoting my member names. This is probably because I was using member names such as "name", "default", and "required".

So, to clarify some questions above: CF9 does allow quoting member names in implicit struct definitions and, depending on the names, may even require it.