Dynamic ColdFusion Variables Via Quoted Naming
Posted July 20, 2006 at 8:18 AM by Ben Nadel
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
I am looking for a way to do exactly this in JavaScript. Any ideas?
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.
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?
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.
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.
^_^
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>
^_^
Scratch that. Close, but no cigar. Still working on it.
^_^
Got it. Your examples of how you used to do it gave me an idea.
#VARIABLES[marketplace & "Files"].recordCount#
Thanks,
^_^
@WolfShade,
Ha ha, no problem - glad you got it working :)
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.
@Vlad,
I think you are trying to reference the column from within the query. If so, try this:
q1[ variable1 ]
Thanks for reply, Ben. Problem was that I had to use evaluate.
This is correct: #evaluate('name_' & variable)#
@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).
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".
@Vlado:
when using array notation on query columns, one must specify a row number as well:
<cfoutput>#q1[variable1][1]#</cfoutput>
Azadi
@Azadi,
Excellent catch! Thanks for pointing that out.
@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)
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?
And the answer is #evaluate(variable_1)#
@Ricelle,
I try to avoid using evaluate() whenever possible. In your case, you simply need to access the variables scope explicitly:
#variables[ variable_1 ]#
You rock! Works like a charm. Thanks for the post.
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?
@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?
Thanks Ben, that's sorted my problem out.
Many thanks
Jim
@Jim,
Awesome.
Thank you thank you thank you . . . works great!
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?
@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?
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?
@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.
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"
@Thomas,
No problem; at that point, you're gonna want to use something like:
<cfif structKeyExists( variables, "#h#Hours" )>
That should do the trick.
Thank you so much for the tip!
mind. blown.
this makes me life oodles easier.
you're the best Ben!
@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.
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>
@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.
Has anyone mentioned the function setVariable(name, value) ?
Or maybe it's not relevant.
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]);
I figured it out. Here is the answer if anyone is interested:
StructInsert(APPLICATION[getTagTypes['tagType'][i]], "tag"&tagCount, getTagTypesAndTags.tag[z]);
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!
This really saved me a lot of time and effort. Thanks!
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
What if you wanted to dynamically create variables inside a session?
example session.#dynamicName#.Something.
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);
}
You save my day... everyday... :-)
Hey Ben! Just wanted to let you know that I found this post useful - even all these years later.
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.
I fully concur with Brad. Thanks as always Ben!
@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!
how to show value variable Girl#intGirl#??
<cfoutput>??????????</cfoutput>
@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.



