When is a Structure not a Structure?

{ Posted By : Eric Cobb on November 4, 2010 }
Related Categories: Tips 'n Tricks, CFML

A few days ago I was working on an application I inherited and needed to clear a structure that was stored in the session. No problem, right? Just use StructClear(). But every time I tried, I got an "Element is undefined" error. I knew the elements were there, I could output them with no problems. But for some reason, StructClear() refused to see my structure.

Then I took a look at how the elements in the structure were being set and I discovered why. As it turns out, what I thought was a structure was actually just a bunch of individual strings, due to the way they were being created with StructInsert(). Take the following example:

<cfset variables.myVar = StructNew()>
<cfset variables.myVar.one = 1>
<cfset variables.myVar.two = 2>
<cfset variables.myVar.three = 3>
<cfset variables.myVar.four = 4>
<cfdump var="#variables#">

This just creates a simple structure named "myVar", which gives us the following results:

Nothing special here, this is exactly what we would expect. Now, lets try the same example using StructInsert().

<cfset variables.myVar = StructNew()>
<cfset StructInsert(variables.myVar, "one", "1")>
<cfset StructInsert(variables.myVar, "two", "2")>
<cfset StructInsert(variables.myVar, "three", "3")>
<cfset StructInsert(variables.myVar, "four", "4")>
<cfdump var="#variables#">

Now if we dump our structure, we get the exact same result, which is still what we expected.

But, in this particular case, the variables were being added via StructInsert() a little differently, like this:

<cfset variables.myVar = StructNew()>
<cfset StructInsert(variables, "myVar.one", "1")>
<cfset StructInsert(variables, "myVar.two", "2")>
<cfset StructInsert(variables, "myVar.three", "3")>
<cfset StructInsert(variables, "myVar.four", "4")>
<cfdump var="#variables#">

This time if we dump our structure, this is what we get:

Notice that our actual myVar structure is empty, and now we have four new string variables created named "myVar.one", etc... This is because in the first StructInsert() example we are specifying "variables.myVar" as the structure, and adding an element named "one". However, in the last example, instead of adding a new element to the "variables.myVar" structure, we are adding a literal string called "myVar.one" to the variables scope.

If you're not used to working with structures this may seem a little confusing, especially when in all three of the code samples above you can output #variables.myVar.one# and get the exact same result. But, in actuality, behind the scenes ColdFusion (and Railo) considers the last example as something totally different from the first two. So much so that you can actually have the two together and it won't cause a conflict:

<cfset variables.myVar = StructNew()>
<cfset StructInsert(variables.myVar, "one", "1")>
<cfset StructInsert(variables, "myVar.one", "1")>
<cfdump var="#variables#">

However, now it seems we have two different types of variables that are named the same thing. When we try to output #variables.myVar.one#, which one of the two does ColdFusion choose? Let's change up the previous example a little to see:

<cfset variables.myVar = StructNew()>
<cfset StructInsert(variables.myVar, "one", "111")>
<cfset StructInsert(variables, "myVar.one", "1")>
<cfdump var="#variables#">
<cfoutput>CF chooses: #variables.myVar.one#</cfoutput>

The answer is:

Michael's Gravatar Interesting that CF allows you to use key names that are not syntactically correct CF variables, though I've seen that sort of behavior in oddly named query columns, so I guess its not that surprising. For any oddly named structure key, you'll have to use the StructureName["KeyName"] syntax to access.

e.g. Variables.myVar.one will contain 111, while Variables["MyVar.one"] will contain 1
# Posted By Michael | 11/4/10 4:09 PM
Dan Wilson's Gravatar As a point of curiosity, why are you using StructInsert?

Why not just use struct.key = "whatever";
# Posted By Dan Wilson | 11/5/10 8:15 AM
Eric Cobb's Gravatar @Michael - yeah, I've been playing around with some of the different variable notation types to see what produces what.

@Dan - I was wondering if someone was going to ask that. ;) This is some really old (CF 4'ish) code that I inherited, and some of the things in it are not exactly done the best way.
# Posted By Eric Cobb | 11/5/10 8:29 AM
Nolan Erck's Gravatar Since you mentioned this is old code, possibly from CF4, it's possible that this syntax "worked" at some point, and the newer parsing engines changed it (remember when CF6 came out, it was a -complete- rewrite from the ground up). I've run into things like that before where (admittedly BAD) code "worked" on CF4 or 5, but died when we upgraded to CF6 or newer. I wonder if that was the case here?
# Posted By Nolan Erck | 11/5/10 3:30 PM
Eric Cobb's Gravatar @Nolan - that is a VERY good point that I had not considered. Thanks for bringing that up!
# Posted By Eric Cobb | 11/5/10 3:40 PM
Michael's Gravatar Yeah, as far as I am aware, pre CF6 it was written in C and then CF6+ was totally re-done in Java.

It's quite understandable that some things may have changed along the way.
# Posted By Michael | 1/15/11 9:07 AM