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 CFUNITED 2009 (Lansdowne, VA) with:

Dynamic ColdFusion Variables Via Quoted Naming

By Ben Nadel on
Tags: ColdFusion

I just came across something very interesting that I have never ever seen before: the creation of dynamic ColdFusion variables via quotes naming. In the past, I have made dynamic variable references using either the Structure-key notation, or if all else fails, the evaluate method.

  • <!--- Create three variables. --->
  • <cfset Girl1 = "not sexy" />
  • <cfset Girl2 = "not sexy" />
  • <cfset Girl3 = "not sexy" />
  •  
  • <!--- Loop over the girls and alter the values. --->
  • <cfloop index="intGirl" from="1" to="3">
  •  
  • <!--- Randomly pick 1 (true) or 0 (false). --->
  • <cfif RandRange( 0, 1 )>
  •  
  • <!--- Set the dynamic variable naming via struct notation. --->
  • <cfset VARIABLES[ "Girl" & intGirl ] = "super sexy" />
  •  
  • <cfelse>
  •  
  • <!--- Set the dynamic variable naming via Evaluate(). --->
  • <cfset Evaluate( "Girl#intGirl# = 'mad ugly'" ) />
  •  
  • </cfif>
  •  
  • </cfloop>

Note: I am a huge fan of scoping almost all my variables and therefore use the structure-key notation for dynamic naming as much as possible. I always view the use of Evaluate() as a sort of cop-out of quick fix for poor planning.

But anyway, back on topic, I just found out that you can create dynamic variable names via quotes:

  • <cfset "some_var_name_here" = "some_value" />

I have never seen quoted values on the left side of an equation before. It doesn't even seem like it would work. But is does! This is how this would look in the above example:

  • <!--- Loop over the girls and alter the values. --->
  • <cfloop index="intGirl" from="1" to="3">
  •  
  • <!--- Randomly pick 1 (true) or 0 (false). --->
  • <cfif RandRange( 0, 1 )>
  •  
  • <!--- Set the dynamic variable naming used quoted evaluation. --->
  • <cfset "Girl#intGirl#" = "super sexy" />
  •  
  • </cfif>
  •  
  • </cfloop>

In that example "Girl#intGirl#" creates the proper variable name reference the proper variable. I suppose this is some sort of evaluation... I am not completely sure how it works as to me, the left side should just evaluate to a string, not a variable reference. Anyway, it's kind of interesting to know that this is even possible in ColdFusion. Not sure if I would ever use it.

If you care about speed testing, on my server, neither were consistent. Sometimes one was faster than the other, sometimes it was the other way. Both are fast though.




Reader Comments

Marc,

You can do refer to variables in the top level scope using the THIS object, which can refer to different things at different times. Look at this:

// Set up three girls.
var strGirl1 = "sarah";
var strGirl2 = "anne";
var strGirl3 = "christina";

// Set dynamic variable.
this[ "strGirl" + 2 ] = "kimmie";

// Alert three girls.
alert( strGirl1 );
alert( strGirl2 );
alert( strGirl3 );

function Test(){
// Set up three girls.
var strGirl1 = "fn - julia";
var strGirl2 = "fn - alex";
var strGirl3 = "fn - maggie";

// Set dynamic variable.
this[ "strGirl" + 2 ] = "fn - connie";

// Alert three girls.
alert( strGirl1 );
alert( strGirl2 );
alert( strGirl3 );
}

// Call test funciton.
Test();

// Alert three ORIGINAL girls.
alert( strGirl1 );
alert( strGirl2 );
alert( strGirl3 );

-----

Notice that in the first THIS reference, I am building the name of the variable as it exists within the window parent object. However, when I call the Test() method, and try to change the variable name using THIS, it DOES NOT set the function-scoped variables. It changes the original, window-scoped variables.

My suggestion is that anytime you want to create dynamic variable name, do so WITHIN an object:

var objVars = new Object();

objVars[ "Girl1" ] = "Jamie";
objVars[ "Girl2" ] = "Steffie";
objVars[ "Girl3" ] = "Gizelle";

objVars[ "Girl" + 2 ] = "Laura";

for (var i = 1 ; i <= 3 ; i++)
alert( objVars[ "Girl" + i ] );

---

This way, you will always be able to use structure object notation when referring to dynamic names.

Reply to this Comment

Hi ben, i'm trying your dynamic variable naming technique and it's not working as expected with my example.
Your example
<!--- Set the dynamic variable naming used quoted evaluation. --->
<cfset "Girl#intGirl#" = "super sexy" />

My example uses a couple of structs, local and myStruct, but that's the only difference..
<cfset "local.myStruct.#temp#" = "some val"

i get the error
"The string "local.myStruct.1" is not a valid ColdFusion variable name",
which seems that coldfusion isn't taking the "" off the desired variable name.

I'd use an array to solve it, except temp isn't going to be sequential numbers and i am not sure how many values are in the list which populates the temp variable as it is returned from a query. any ideas how to work it within structs?

Reply to this Comment

Jono,

The issue here is that your struct keys are numbers. While this isn't a problem when using array notation (ie. objStruct[ 1 ] =...), variable names in ColdFusion cannot start with numbers. When you try to go with the dynamic names, it can only use proper variables names.

If you change the naming convention slightly, to have a letter before it, it will work:

<cfset LOCAL.Struct.a1 = "test" />

<cfset "LOCAL.Struct.a#(1)#" = "another test" />

This should work since the thirst level variable name starts with "a" which is valid, not a number, which is invalid.

Reply to this Comment

I'm trying to set up a list loop that will list the specific contents of a directory according to a market name, as well as the recordcount (from a CFDIRECTORY) for each. I know I dont' HAVE to give each iteration query a different name, but for keeping things straight, I'd like to. I'm already setting the queryname using quotes, as per your suggestion; but how can I display said query name, or reference the recordcount of the queries if the name keeps changing?

Thank you, so much, for your site.

^_^

Reply to this Comment

Got it. Had to play with it a bit, but I got the answer I was looking for:

<cfdirectory directory="#dirName#" action="list" name="#marketname#Files" filter="*#marketname#*.txt" sort="name ASC">
<cfif "#marketname#Files.recordCount" gt 0>
<cfoutput>Files found: #"#marketname#Files.recordCount"#<br><cfloop query="#marketname#Files">#name#<br></cfloop></cfoutput>
<cfelse>
<cfoutput>No files found for #marketname#.</cfoutput>
</cfif>

^_^

Reply to this Comment

Got it. Your examples of how you used to do it gave me an idea.

#VARIABLES[marketplace & "Files"].recordCount#

Thanks,

^_^

Reply to this Comment

I'm trying to create name of variable1 from string and another variable2 - for example APPLICATION.language.

<cfset variable1 = 'name_' & APPLICATION.language>

and then use is as a table in query

<cfquery name ="q1">SELECT * FROM table</query>
<cfoutput query="q1">#variable1#</output>

BUT problem is that output is not value from database but value of variable1, for example 'name_en'.

Any suggestions?
Thank you.

Reply to this Comment

@Vlad,

I think you are trying to reference the column from within the query. If so, try this:

q1[ variable1 ]

Reply to this Comment

Thanks for reply, Ben. Problem was that I had to use evaluate.
This is correct: #evaluate('name_' & variable)#

Reply to this Comment

@Vlado,

Did you try the query[] notation? I find that using evaluate() usually means that something else isn't working properly (not all the time, but I use it with caution).

Reply to this Comment

Looks like query notation doesnt work for me.
Here is my example tha works:
<cfoutput query="q1">#evaluate('string_' & APPLICATION.language)# </cfoutput>
This doesnt work:
<cfoutput>#q1['string_' & APPLICATION.language]# </cfoutput>
Error:Complex object types cannot be converted to simple values.

I just want to make clear that " 'string_' & APPLICATION.language " create name of column in my table "string_en".

Reply to this Comment

@Vlado:
when using array notation on query columns, one must specify a row number as well:

<cfoutput>#q1[variable1][1]#</cfoutput>

Azadi

Reply to this Comment

@Azadi,

thank you , I've tried your example:
<cfoutput query ="q1">
#q1['string_' & APPLICATION.language][2]#
</cfoutput>
and result is the same value.
It looks like it does not loop (i dont want to use cfloop)

Reply to this Comment

I have created a variable "variable_1" that contains the name of a dynamic variable. I want to output the value of variable_1 as a variable...
ex.<cfset variable_1 = "group_#icounter#">
<cfoutput>#variable_1#</cfoutput>
this obviously outputs the value as text but i want to output the name to extract data from an existing variable with the same variable name. I hope this makes sence?

Reply to this Comment

@Ricelle,

I try to avoid using evaluate() whenever possible. In your case, you simply need to access the variables scope explicitly:

#variables[ variable_1 ]#

Reply to this Comment

Hi Ben,

How about this one. I've got an array of structs I want to populate from a columnlist.

<cfset arrayAppend(args.tempPeople,structNew())>
<cfloop list="#columnlist#" delimiters="," index="field">
<cfset "args.tempPeople[#arrayLen(args.tempPeople)#].#field#" = evaluate("args.qryGetMembers.#field#")>
</cfloop>

It doesn't seem to like this, any ideas?

Reply to this Comment

@Jim,

Queries can be referenced using array notation:

query[ "columnName" ][ rowIndex ]

This allows you to create dynamic values for both the column name and the row index. Given that, you can probably do something like:

<cfset args.tempPeople[ arrayLen( args.tempPeople ) ][ field ] = args.qryGetMembers[ field ][ args.qryGetMembers.currentRow ] />

Does that help?

Reply to this Comment

Ok, I am having an issue with this on few different levels. I am trying to sum hours between two time values by hour. So, here is my function:

<cfoutput>
<cfset startH = timeformat('08:00:00','HH:MM:SS')>
<cfset endH = timeformat('09:00:00','HH:MM:SS')>
<cfloop from="8" to="24" index="h">
<cfloop query="qrySchedule">
<cfif #end# GT #endH#>
<cfif isDefined(#evaluate(h & 'Hours')#)>
<cfset #evaluate(h & 'Hours')# = #evaluate(h & 'Hours')# + 1 />
<cfelse>
<cfset #evaluate(h & 'Hours')# = #evaluate(h & 'Hours')#>
</cfif>
<cfelse>
<cfif isDefined(#evaluate(h & 'Hours')#)>
<cfset #evaluate(h & 'Hours')# = #evaluate(h & 'Hours')# + #datediff('n',startH,end)#>
<cfelse>
<cfset #evaluate(h & 'Hours')# = #datediff('n',startH,end)#>
</cfif>
</cfif>
#evaluate(h & 'Hours')#
</cfloop>
<cfset startH = #dateadd('h',1,startH)#>
<cfset endH = #dateadd('h',1,endH)#>
</cfloop>
</cfoutput>

The error it throws is:
"Can not assign a value to a function.
Unable to assign a value to the function "evaluate" on line 511, column 41"

Any ideas on how I can achieve what I am attempting to do?

Reply to this Comment

@Thomas,

You're not going to want to use evaluate() for this kind of stuff - that will try to actually execute the variable name after it concatenates it; you want to create it, not evaluate it.

Rather than doing something like:

<cfset #evaluate(h & 'Hours')# = xyz />

... you probably want to use the quoted variable approach:

<cfset "#h#Hours" = xyz />

Does that help clarify?

Reply to this Comment

It does, and I have tried it, however, I am having issues outputting it...

<cfoutput>
"#h#Hours#" //Returns 8Hours rather than the value from the cfset
</cfoutput>

I've also tried:
##h#Hours#
#"#h#Hours"#

Any ideas on the output?

Reply to this Comment

@Thomas,

At that point, you can typically use the parent scope (ex. Variables) to output it.

#variables[ "#h#Hours" ]#

You might also be able to use evaluate:

#evaluate( "#h#Hours" )#

... But I am not sure about that last tip.

Reply to this Comment

Perfect! I really appreciate your quick responses. I have one last issue... Running it through an isDefined:

<cfif isDefined(#variables[ "#h#Hours" ]#)>

"Element 8Hours is undefined in a Java object of type class coldfusion.runtime.VariableScope"

Reply to this Comment

@Thomas,

No problem; at that point, you're gonna want to use something like:

<cfif structKeyExists( variables, "#h#Hours" )>

That should do the trick.

Reply to this Comment

@Kate,

Ha ha, thanks :) I was pretty excited when I found out about this one. It's one of those features that you don't need alll that often; but, when it comes up, it's simply awesome that the language supports it.

Reply to this Comment

Hi Ben,

Thanks for the tips. I was messing with this to see where else it could be used and I ran into a problem. It seems like you can use the dynamic naming for query names but there does not seem to be a way to access the query that I could find. I tried evaluate but i kept getting an error saying the variable name did not exist.

<cfset Table = "Customers">

<cfquery name="getDataFrom#Table#" datasource="#somedb#">
select * sometable
</cfquery>

<cfoutput>
<cfset data = evaluate("getDataFrom#Table#.CustomerID")>
</cfoutput>

Reply to this Comment

@John,

By default, all of your non-scoped variables are stored in the implicit Variables scope. As such, you can always access them this way:

<cfset myQuery = variables[ "getDataFrom#table#" ] />

This will give you more "manageable" reference to the query object, at which point, you can treat it just like you would any other query variable.

Reply to this Comment

Great post, Ben!

I can't seem to figure out to get the following right... keep getting "You have attempted to dereference a scalar variable of type class java.lang.String as a structure with members." I'm doing a loop within a loop of 2 queries to get the "tagType" as the name of the structure with the "tag" as the value of the structure. Any suggestions would be greatly appreciated!

StructInsert('application.#getTagTypes.tagType[i]#',getTagTypes.tagType[i]&tagCount,getTagTypesAndTags.tag[z]);

Reply to this Comment

I figured it out. Here is the answer if anyone is interested:

StructInsert(APPLICATION[getTagTypes['tagType'][i]], "tag"&tagCount, getTagTypesAndTags.tag[z]);

Reply to this Comment

This little tidbit just pulled my butt right out of a sling in a comparison statement.

<cfif "agingCount.#searchOption#" EQ #thisSearchSecond.resultSecond#>

I would never have believed it could be that simple. Broke every other way I tried it.

THANK YOU!

Reply to this Comment

Hi Ben:
I have used it numerous times especially for naming the checkboxes, select boxes and defaulting it to it's selected option.

<CFLOOP index="loopCtr" from="1" To="10">
<CFSET checkBoxName="chk"&#loopctr#>
</CFLOOP>
While getting the data you need to use evaluate fn, otherwise it will get the name of the checkbox

Reply to this Comment

Ben, this was exactly what I was looking for. Thanks for the great explanation!

LOCAL.Product_ID_Compare_1 = "";
LOCAL.Product_ID_Compare_2 = "";
LOCAL.Product_ID_Compare_3 = "";
LOCAL.Product_ID_Compare_4 = "";
LOCAL.CleanList = ARGUMENTS.CleanList;
LOCAL.CleanListLen = listLen(LOCAL.CleanList);
for (LOCAL.i = 1; LOCAL.i lte LOCAL.CleanListLen; LOCAL.i++) {
"LOCAL.Product_ID_Compare_#LOCAL.i#" = listGetAt(LOCAL.CleanList, LOCAL.i);
}

Reply to this Comment

For those of us iterating through form input, the notation from Ben's Jul 22, 2010 comment above also works. For example, when updating a database from form data that comes to your action page looking like this:

Form.Color1 = "red"
Form.Color2 = "white"
Form.Color3 = "blue"
Form.TitleID3 = "1"
Form.TitleID3 = "2"
Form.TitleID3 = "3"

use

  • <cfloop from="1" to="#Form.NUMBEROFREVIEWS#" index="i">
  • <cfquery datasource="Clients">
  • UPDATE ReadAllSummerTitles
  • SET Color = <cfqueryparam cfsqltype="cf_sql_varchar" value="#StructGet("Form.Color#i#")#">
  • WHERE TitleID = <cfqueryparam cfsqltype="cf_sql_int" value="#StructGet("Form.TitleID#i#")#">
  • </cfquery>
  • </cfloop>

alternatively,

value="#Form("Color#i#")#"

I welcome suggestions for working with this issue more elegantly.

Reply to this Comment

@Brad, @Grant,

Awesome! I'm happy to hear this is still good after all this time.

Did you also know that in later versions of ColdFusion, you can use this same kind of approach for dynamic component paths used for component invocation:

  • var myComponent = new "#cfcPath#"();

Pretty cool stuff!

Reply to this Comment

@Adrian,

You would either know which variable you are referencing at the time, and you can explicitly use it:

<cfoutput>#Girl2#</cfoutput>

... or, you can always use array-notation in the container scope. So, for example, unless you are using an explicit scope like Request or Local, your variables will always be implicitly declared in the Variables scope (both in regular pages and in ColdFusion components). As such, you can refer to the dynamic variable like this:

<cfoutput>#variables[ "Girl#intGirl#" ]#</cfoutput>

This is saying, evaluate the key, "Girl#intGirl#", and then look it up in the struct, Variables.

Reply to this Comment

This is definitely one of my fav. threads. I couldn't at first tho, figure out how to use this technique with more complex variable structures. It wasn't initially obvious to me, so I thought I'd add what I found.

I was using dynamic naming of variables in session/application scopes in the form of user preferences and application preferences. What I had trouble doing at first was using the values of the dynamically named session variables,for example:

<cfset "session.user.custompref#x#"="arbitrary value">

when X = 1

I couldn't figure out how to return the value of what was session.user.custompref1

At first I tried:
<cfset prevalue=session[ "user.custompref#x#" ]>

Then I tried:

<cfset prevalue=session.user[ "custompref#x#" ]>
when x=1 as in the above example.

The latter working.

As side note - Ben - this original post was in 2006 - love how its the kind of gift that keeps on giving!

Reply to this Comment

This is first part is of course, ok for me:
<cfset SMstructure.MARKETDATES[1].PUBLISHINGDATEROLE_ID =4>

My structure is updated with the value of 4 in the correct place on the struct of the first element of the Marketdates array.

what I'm stuck on is how to get this part to be dynamic:
.MARKETDATES[1].PUBLISHINGDATEROLE_ID

where only SMstructure is known, the rest of the name comes from a variable called "branch"
<cfset branch = ".MARKETDATES[1].PUBLISHINGDATEROLE_ID">

<cfset 'SMstructure#branch#' =4> does not work.

Any ideas? Your posts usually inspire some solutions, however this one has me stuck. Thank you.

Reply to this Comment

BB,
I tried your example and found out something interesting.

First I have to create the array and test it.

<cfset SMstructure.MARKETDATES=arraynew(1)>
<cfset SMstructure.MARKETDATES[1]=structnew()>
<cfset SMstructure.MARKETDATES[1].PUBLISHINGDATEROLE_ID =4>
<cfdump var="#SMstructure#" expand="yes" label="Setup expression">

The cfdump looks like what I expected to see.

Then I tried to create the whole thing using the method in the thread:
<cfset x=1>
<cfset branch = "MARKETDATES[#x#].PUBLISHINGDATEROLE_ID">
<cfset SMstructure["#branch#"] =8>
<cfdump var="#SMstructure#" expand="yes" label="Recreate Expression With Array Dynamically = Fail">

In the dump you can see that the var name is evaluated as a string, not as an array.

So then I tried this which moves the array to the left of the []... and I get soemthing that does recreate the array.

<cfset x=1>
<cfset branch = "PUBLISHINGDATEROLE_ID">
<cfset SMstructure.MARKETDATES[x]["#branch#"] =8>
<cfdump var="#SMstructure#" expand="yes" label="Recreate Expression Without Array Dynamically=Success">

So, my conclusion is that with this method you can not dynamically create an array in the format you want because it is seen as a literal string.

Hope this helps.

Reply to this Comment

@Tony,

Thanks for looking at this Tony. If I could get MARKETDATES[x] to also be variable, I'm almost there. I have more arrays on the structure than just marketdates.

The large structure and arrays are already created. I need a simple way to update a specific value of any array's struct on the big structure.
So "SMstructure" in the variable name is constant but the part to update is not.
I have a string variable called branch that represents the part to update, that may be :
"MARKETDATES[1].PUBLISHINGDATEROLE_ID"
or
"PRICES[3].CURRENCY_ID"

Reply to this Comment

@Tony,

<cfset mywholepath= "SMstructure.MARKETDATES[1].PUBLISHINGDATEROLE_ID">
<cfset '#evaluate(mywholepath)#' = 8> This does work, but we're supposed to avoid evaluate, right?

Breaking it down so each part of the path and its array number are in their own variables also works, but it could be an unknown number of deeper nested arrays.
<cfset mypath1= "MARKETDATES">
<cfset mypath1x= "1">
<cfset mypath2= "DETAILS">
<cfset mypath2x= "2">
<cfset mypathend= "TYPE_ID">

<cfset SMstructure['#mypath1#'][#mypath1x#]['#mypath2#'][#mypath2x#]['#mypathend#'] =7>

In the first case above would evaluate be ok? Is it the only solution?

Reply to this Comment

Eureka! The secret is structget. Sweet, when I finally grasped it. Here is the solution:

Goal: update a known section of the structure (variable "thepath") with a new value:

<cfset SMstructure.MARKETDATES=arraynew(1)>
<cfset SMstructure.MARKETDATES[1]=structnew()>
<cfset SMstructure.MARKETDATES[1].PUBLISHINGDATEROLE_ID =4>
<cfset SMstructure.MARKETDATES[1].ANOTHERFIELD =8>
<cfset SMstructure.ANTOHERARRAY=arraynew(1)>
<cfset SMstructure.ANTOHERARRAY[1]=structnew()>
<cfset SMstructure.ANTOHERARRAY[1].SOMETHINGELSE ='test'>
<cfdump var="#SMstructure#">

<cfset thepath="SMstructure.MARKETDATES[1]">

<cfset mytemp=structget(thepath)>
<cfset mytemp.PUBLISHINGDATEROLE_ID=1000>

<cfdump var="#SMstructure#">

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.