Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with:

Learning ColdFusion 9: Implicit Struct And Array Usage

By Ben Nadel on
Tags: ColdFusion

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:

 
 
 
 
 
 
ColdFusion 9 Implicit Struct And Array Creation. 
 
 
 

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:

 
 
 
 
 
 
ColdFusion 9 Implicit Struct And Array Creation Can Be Used Directly In Methods Calls As Arguments. 
 
 
 

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:

 
 
 
 
 
 
ColdFusion 9 Implicit Struct And Array Creation Can Be Used Directly In Tag Attributes. 
 
 
 

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:

 
 
 
 
 
 
ColdFusion 9 Implicit Struct And Array Creation Still Has Order Of Operations Bug Which Evaluates Left Hand Side First Causing Unexpected Errors. 
 
 
 

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:

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




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.

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

@Keith,

From what it sounds like, there is no way to keep keys in-case with implicit creation as you cannot currently quote keys.

Reply to this Comment

@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)) />

Reply to this Comment

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

Reply to this Comment

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!

Reply to this Comment

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.

Reply to this Comment

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

Reply to this Comment

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

Reply to this Comment

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.

For example this code:

<cfset StructWithQuotedKeys = {
"ElementOne" = "Element One",
"ElementTwo" = "Element Two",
"ElementThree" = "Element Three"
}/>

<cfset StructWithLiteralKeys = {
ElementOne = "Element One",
ElementTwo = "Element Two",
ElementThree = "Element Three"
}/>

<cfoutput>
Struct w/ Quoted Keys:<br/>
<cfloop collection=#StructWithQuotedKeys# item="key">
Key: #key#, Value: #StructWithQuotedKeys[key]#<br/>
</cfloop>
<br/>
Struct w/ Literal Keys:<br/>
<cfloop collection=#StructWithLiteralKeys# item="key">
Key: #key#, Value: #StructWithLiteralKeys[key]#<br/>
</cfloop>
</cfoutput>

Has this output when run:

Struct w/ Quoted Keys:
Key: ElementTwo, Value: Element Two
Key: ElementOne, Value: Element One
Key: ElementThree, Value: Element Three

Struct w/ Literal Keys:
Key: ELEMENTTWO, Value: Element Two
Key: ELEMENTONE, Value: Element One
Key: ELEMENTTHREE, Value: Element Three

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

Reply to this Comment

@Graeme,

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:

http://www.bennadel.com/blog/1993-Using-Dynamic-Keys-In-ColdFusion-9-s-Implicit-Struct-Creation.htm

I thought it was great that it could allow for dynamic key names in implicit struct creation; it never even occurred to me that it also maintained key-case - very cool stuff!

Reply to this Comment

@Ben,

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

Reply to this Comment

@Graeme,

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!

Reply to this Comment

Just want to point out that "case-sensitive" isn't really the right term for struct keys created using bracket notation. Try this:

  • s = structNew();
  • s['foo'] = 42;
  • s['FOO'] = 999;

If you now dump s, you'll see it has ONE key, lowercase foo, whose value is 999.

CF may read the keys back to you using the case with which they're originally created, but structs aren't case sensitive in the sense of supporting multiple keys differing only by case.

Java Map and related objects ARE really case sensitive. Do this:

  • m = CreateObject('java', 'java.util.HashMap');
  • m.put('foo', 42);
  • m.put('FOO', 999);
  • WriteOutput(m.toString());

That actually does have two keys, with the same name but different case.

Reply to this Comment

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.

Here's a sample from the code I was trying to execute.

  • fieldTemp =
  • { "name" = "emailAddress", "default" = "",
  • "validate" = {
  • "regex" = {
  • "when" = "Form.emailSource EQ 'new'",
  • "test" = "(?i)^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$",
  • "msg" = "errInvalidValue",
  • "subs" = [ { "from" = "FIELD_NAME", "to" = "new email address for the contact person" } ]
  • },
  • "required" = {
  • "when" = "Form.emailSource EQ 'new'",
  • "test" = "Len(Trim(Attributes.emailAddress)) GT 0",
  • "msg" = "errRequiredFieldEmpty",
  • "subs" = [ { "from" = "FIELD_NAME", "to" = "new email address for the contact person" } ]
  • }
  • }
  • }

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.