Dynamic variable containing numbers - coldfusion

I've tried everything I've found that might be applicable to this, without success to date.
I have variables that look like
x000Foo
I'm trying to create one of these dynamically in form scope from query results and have tried the following, and a few others, without success:
<cfloop query="qFormFields">
<cfset "form.x000#fieldname#" = 0>
You have attempted to dereference a scalar variable of type class coldfusion.sql.QueryColumn as a structure with members
<cfset "form.x[000]#fieldname#" = 0>
The value x000AA_report cannot be converted to a number.
<cfset form["x000#fieldname#"] = 0>
The value x000AA_report cannot be converted to a number.
</cfloop
I know it's related to the zeros, but I'm not sure how to get around it without resorting to renaming these variables throughout the application.
I'm on ColdFusion2016

I'm not sure if this is what you're trying to do: but here's how you can do dynamic variables:
<cfset fieldname = "foo">
<cfset form["x000" & fieldname] = 0>
<cfdump var="#form#">
<!--- variable form.x000Foo = 0 --->
Runnable example on TryCF.com

you can try evaluate function read more
<cfloop query="qFormFields">
<cfset fieldvalue = Evaluate("form.x000#fieldname#")>
<cfdump var="#fieldvalue#">
</cfloop>
let me know if it is working or not.

Related

Parse the contents of a Coldfusion variable into separate variables

OK, so I've been banging my head against this one for a while and getting nowhere. I've been attempting to take the contents of a variable and parse the contained string into parts that would then be ingested into 5 separate variables. Seems simple enough right? Well, it has not proven to be simple at all, at least for me.
So I have a variable (PageContent) that contains the trimmed content from a CFHTTP request.
The PageContent variable now contains:
<tdnowrapalign=right>07/18/2020 13:00</td>
<tdalign=right>1002.12</td>
<tdalign=right>2,874,887</td>
<tdalign=right>12,766</td>
<tdalign=right>13,038</td>
It seems like there should be an easy way to write a loop that would loop over the tags in the "PageContent" variable assigning the content of each tag to a different variable. But every way I try to parse the data in the variable I either get an error (Complex object types cannot be converted to simple values.) or I end up with the content that I originally had in the "PageContent" variable repeated within the loop.
For instance, if I had a loop that would run through 5 iterations and could grab the contents of the tags assigning each to a variable then the desired result would be:
DateTime = "07/18/2020 13:00"
Elevation = "1002.12"
Storage = "2,874,887"
Outflow = "12,766"
Inflow = "13,038"
After trying every example I could find here and elsewhere online I'm now on something like my 100th attempt. Now I'm trying to use regular expressions to grab the contents of the tags and assign them to variables but no luck there. What I ended up with was the entire contents of the PageContent variable being stuffed into each one of the variables. The result was not really unexpected since I don't know of a way to differentiate between the 3 identical "tdalign" tags, but still it seems like at least the first variable would have worked since the tag was different "tdnowrapalign".
<cfset i=5/>
<cfloop index = "LoopCount" from = "1" to = #i#>
<cfif i EQ 1>
<cfset dataDateTime = Replace(PageContent, "<[tdnowrapalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 2>
<cfset elevation = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 3>
<cfset storage = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 4>
<cfset outflow = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
<cfelseif i EQ 5>
<cfset inflow = Replace(PageContent, "<[tdalign][^>]*>(.+?)</[td]>","","ALL")>
</cfif>
<cfoutput>
<cfif isdefined("dataDateTime")>
dataDateTime = #dataDateTime#<br>
</cfif>
<cfif isdefined("elevation")>
elevation = #elevation#<br>
</cfif>
<cfif isdefined("storage")>
storage = #storage#<br>
</cfif>
<cfif isdefined("outflow")>
outflow = #outflow#<br>
</cfif>
<cfif isdefined("inflow")>
inflow = #inflow#<br>
</cfif>
</cfoutput>
<cfset i = i - 1>
</cfloop>
Does anyone know if there is a way to get to the desired outcome I described where I end up with 5 variables containing the contents of the tags contained within the "PageContent" variable?
One way of doing it would be like this
<cfset PageContent = '<tdnowrapalign=right>07/18/2020 13:00</td>
<tdalign=right>1002.12</td>
<tdalign=right>2,874,887</td>
<tdalign=right>12,766</td>
<tdalign=right>13,038</td>' />
<cfset data = ListToArray(PageContent, '</td>', false, true) />
<cfset DateTime = ListLast(data[1], '>') />
<cfset Elevation = ListLast(data[2], '>') />
<cfset Storage = ListLast(data[3], '>') />
<cfset Outflow = ListLast(data[4], '>') />
<cfset Inflow = ListLast(data[5], '>') />
Demo: https://trycf.com/gist/b4f3b630bd1cbdc505d07a7d79b68ef5/acf?theme=monokai

How to refer to variables when the names are indefinite and must be found by regex first, using Coldfusion?

I have an html form that collects variables that look like:
ent1_label
ent2_label
ent1att1_label
ent1att2_label
ent2att1_label
ent2att2_label
ent1att1val1_label
ent1att1val2_label
...
ent2att2val2_label
I don't know in advance how many of ent, att, or val will be coming from the form to the action script. In the input form, I run loops that concatenate the variable names:
ent<cfoutput>#i#</cfoutput>att<cfoutput>#j#</cfoutput>val<cfoutput>#k#</cfoutput>_label
(where i, j, and k are the number of ent, att, and val)
This works great on the input form, but then I'm lost on how to refer to them in the action script.
I've played around with #form.fieldnames# which has all of the actual variable names.
<cfset #formfields# = listToArray(#form.fieldnames#, ",")>
<cfset #formlength# = arraylen(#formfields#)>
<cfset #entattval_label# = arrayNew(1)>
<cfloop from="1" to="#formlength#" index="i">
<cfif REfind("ENT[0-9]*ATT[0-9]*VAL[0-9]*_LABEL", #formfields[i]#) EQ 1>
<cfset arrayAppend(entattval_label, "#formfields[i]#")>
</cfif>
</cfloop>
will make separate arrays for each subset of variables I need. But how do I make it print the contents of the variables, instead of their names?
<cfset #label_length# = arraylen(#entattval_label#)>
<cfloop from="1" to="#label_length#" index="i">
<cfoutput>#entattval_label[i]#</cfoutput>
</cfloop>
You can reference a variable with a dynamic name using what's called array notation:
foo.bar can also be written as foo['bar'] - even though it's a structure and not an array.
This means that you can inject any dynamic name into your reference that you like; and this also works for built in variable scopes like form, url, etc:
For example: form['ent#i#att#j#val#k#_label'] where i, j, and k are the integers you need.
If you find that hard to read, you could also write it as:
form['ent' & i & 'att' & j & 'val' & k & '_label']
In these situations, if the form is being expanded on the client-side (e.g. with JavaScript) without server involvement, I often find it's easiest to include a numeric hidden field (or in your case, maybe 3?) to indicate the ranges for i, j, and k.
(Too long and too much formatting for an easily-readable comment.)
Just for the record, you can significantly shorten your code by using ReMatchNoCase() instead of ReFind() if you're using CF8+ or Railo.
ReMatchNoCase (and ReMatch of course) looks for as many matches as possible. I created a form.fieldnames variable for demonstration but that's not even really needed
<cfset form.fieldnames = "ent1_label,ent2_label,notvalid,btnSelect,ent1att1_label,ent1att2_label,ent2att1_label,ent2att2_label,ent1att1val1_label,ent1att1val2_label,ent2att2val2_label">
<Cfset NamesArray = REMatchNoCase("ENT[0-9]*ATT[0-9]*VAL[0-9]*_LABEL",form.fieldnames)>
<cfoutput><cfloop from="1" to="#ArrayLen(NamesArray)#" index="i">
#NamesArray[i]#: #form[NamesArray[i]]#<br />
</cfloop>
</cfoutput>

ColdFusion isDefined

I am trying to check to see if data exist in my form If data does not exist I want to assign it to O. How can I do this.
<cfif not isDefined("FORM.Age")>
cfset FORM.Age = "0"
<cfif>
Generally the best practice is considered to be to avoid isDefined. This is because isDefined will search all scopes until it finds a matching variable. So it's more efficient to use structKeyExists, eg:
<cfif NOT structKeyExists(form, "age")>
<cfset form.age = 0>
</cfif>
Also, another way to achieve this is to use cfparam, and specify 0 as the default:
<cfparam name="form.age" default="0">
You're almost there:
<cfif not isDefined("FORM.Age")>
<cfset Form.Age = 0>
</cfif>
Technically what you have is fine once you enclose the cfset in tags < and >. Assuming that omission is just a typo, could it be you are trying to use it with a text field?
Text fields always exist on submission. The value may be an empty string, but the field itself still exists, so IsDefined will always return true. If that is the case, you need to examine the field length or value instead. Then do something if it is empty according to your criteria. For example:
<!--- value is an empty string --->
<cfif NOT len(FORM.age)>
do something
</cfif>
... OR
<!--- value is an empty string or white space only --->
<cfif NOT len(trim(FORM.age))>
do something
</cfif>
... OR
<!--- convert non-numeric values to zero (0) --->
<cfset FORM.Age = val(FORM.Age)>
There are actually two things you want to to ensure. First, make sure this page was arrived at by submitting the proper form. Next, ensure you have a numeric value for the form.age variable. Here is an example of how you might want to code this:
<cfif StructKeyExists(form, "age") and cgi.http_referrer is what it should be>
<cfif IsNumeric(form.age) and form.age gt 0>
<cfset AgeSubmitted = int(form.age)>
<cfelse>
<cfset AgeSubmitted = 0>
</cfif>
...more code to process form
<cfelse>
...code for when page was not arrived at properly
</cfif>

ColdFusion structs Direct Assignment vs object literal notation

The newer versions of ColdFusion (I believe CF 8 and 9) allow you to create structs with object literal notation similar to JSON.
My question is, are there specific benefits (execution efficiency maybe) to using object literal notation over individual assignments for data that is essentially static?
For example:
With individual assignments you would do something like this:
var user = {};
user.Fname = "MyFirstnam";
user.Lname = "MyLastName";
user.titles = [];
ArrayAppend(user.titles,'Mr');
ArrayAppend(user.titles,'Dr.');
Whereas with object literals you would do something like.
var user = {Fname = "MyFirstnam",
Lname = "MyLastName",
titles = ['Mr','Dr']};
Now this limited example is admittedly simple, but if titles was an array of structures (Say an array of addresses), the literal notation becomes awkward to work with.
Before I tried anything, I thought straight away that literals would be faster, as you create everything you need in runtime, and don't need to waste time creating variables, then calling functions to append and all that.
I then wrote a little test that produces a chart with the results. You got me curious there :-)
The results prove I was right, as the graph shows a staggering difference as you can see:
But remember that although one would jump and go with literal notation, I think it's important to remember that literal notation can be awkward, and will most of the times confuse more.
Obviously, if you are developing a page that really needs the speed boost, literal notation is what you're looking for, but be aware that sometimes on CF8, it will produce some strange behaviour.
Just to show you the sort of tests I run:
<cfset aLiterals = arrayNew(1) />
<cfset aDirect = arrayNew(1) />
<cfsilent>
<cfloop from="1" to="10000" index="mm">
<!--- LITERAL --->
<!--- start timer --->
<cfset start = getTickcount() />
<cfloop from="1" to="1000" index="ii">
<cfset user = {Fname = "MyFirstnam", Lname = "MyLastName", titles = ['Mr','Dr']} />
</cfloop>
<!--- end timer --->
<cfset end = getTickCount()>
<!--- Display total time --->
<cfset total = end-start>
<cfset arrayAppend(aLiterals,total) />
<!--- DIRECT --->
<!--- start timer --->
<cfset start1 = getTickcount() />
<cfloop from="1" to="1000" index="jj">
<cfset user = {} />
<cfset user.Fname = "MyFirstnam" />
<cfset user.Lname = "MyLastName" />
<cfset user.titles = [] />
<cfset ArrayAppend(user.titles,'Mr') />
<cfset ArrayAppend(user.titles,'Dr.') />
</cfloop>
<!--- end timer --->
<cfset end1 = getTickCount()>
<!--- Display total time --->
<cfset total1 = end1-start1>
<cfset arrayAppend(aDirect,total1) />
</cfloop>
</cfsilent>
<!--- The cfchart --->
<cfchart format="png" xaxistitle="function" yaxistitle="Loading Time (in secs.)">
<cfchartseries type="bar" serieslabel="literal">
<cfchartdata item="literal" value="#arrayAvg(aLiterals)#">
</cfchartseries>
<cfchartseries type="bar" serieslabel="direct">
<cfchartdata item="direct" value="#arrayAvg(aDirect)#">
</cfchartseries>
</cfchart>
Hope this helps you.
Literal notation is declarative programming, not procedural programming.
With literal notation, you tell the computer what it is you want clearly and in a single step. Without literal notation, you build what you want slowly, piece by piece, and without clarity.
Note that literal notation in CF8 is awkward and flawed, at best. It should rarely be used, and then only in simple cases. The literal notation in CF9 is fine.
For CF8, you can define helpers:
function $A() {
var r = [ ];
var i = 0;
var m = ArrayLen(Arguments);
for(i = 1; i <= m; i += 1)
ArrayAppend(r, Arguments[i]);
return r;
}
function $S() {
return StructCopy(Arguments);
}
And use them as such:
var user = $S(
Fname = "MyFirstnam",
Lname = "MyLastName",
titles = $A('Mr', 'Dr')
);
These helpers work all the time, preserve struct key case (struct keys are not simply uppercased but are cased as you typed them), and nest recursively without bound.

When should I use # in ColdFusion?

This has been one of the biggest obstacles in teaching new people ColdFusion.
When to use # is ambiguous at best. Since using them doesn't often create a problem it seems that most people gravitate to using them too much.
So, what are the basic rules?
I think it may be easier to say where NOT to use #. The only place is in cfif statements, and cfset statements where you are not using a variable to build a string in quotes. You would need to use the # sign in almost all other cases.
Example of where you are not going to use it:
<cfset value1 = 5>
<cfset value2 = value1/>
<cfif value1 EQ value2>
Yay!!!
</cfif>
<cfset value2 = "Four plus one is " & value1/>
Examples of where you will use #:
in a cfset where the variable is surrounded by quotes
<cfset value1 = 5>
<cfset value2 = "Four plus one is #value1#"/>
the bodies of cfoutput, cfmail, and cffunction (output="yes") tags
<cfoutput>#value2#</cfoutput>
<cfmail to="e#example.com" from="e#example.com" subject="x">#value2#</cfmail>
<cffunction name="func" output="yes">#value2#</cffunction>
in an attribute value of any coldfusion tag
<cfset dsn = "myDB"/>
<cfquery name="qryUsers" datasource="#dsn#">
<cfset value1 = 5>
<cfset value2 = 10/>
<cfloop from="#value1#" to="#value2#" index="i">
<cfqueryparam value="#value1#" cfsqltype="cf_sql_integer"/>
EDIT:
One oddball little thing I just noticed that seems inconsistent is conditional loops allow the variable name to be used with and without # signs.
<cfset value1 = 5>
<cfloop condition = "value1 LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
<cfset value1 = 5>
<cfloop condition = "#value1# LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
Here's what Adobe has to say about it:
Using number signs
String interpolation:
<cfset name = "Danny" />
<cfset greeting = "Hello, #name#!" />
<!--- greeting is set to: "Hello, Danny!" --->
Auto-escaped string interpolation in cfquery:
<cfset username = "dannyo'doule" ?>
<cfquery ...>
select u.[ID]
from [User] u
where u.[Username] = '#username#'
</cfquery>
<!--- the query is sent to the server (auto-escaped) as: --->
<!--- select u.[ID] from [User] u where u.[Username] = 'dannyo''doule' --->
<!--- note that the single-quote in the username has been escaped --->
<!--- by cfquery before being sent to the database server --->
Passing complex arguments/attributes in CFML:
<cfset p = StructNew() />
<cfset p.firstName = "Danny" />
<cfset p.lastName = "Robinson" />
<cfmodule template="modules/view/person.cfm" person="#p#">
<!--- the variable Attributes.person will be --->
<!--- available in modules/view/person.cfm --->
Passing complex arguments requires # signes only in CFML, not CFScript. In addition, you can pass any kind of value: simple values, arrays, structs, cfcomponents, cffunctions, java objects, com objects, etc.
In all these cases, the text between the # signs does not have to be the name of a variable. In fact, it can by any expression. Of course, for string interpolation, the expression must evaluate to a simple value, but for argument/attribute passing in CFML, the expression may evaluate to any complex value as well.
The #...# syntax allows you to embed an expression within a string literal. ColdFusion is unfortunately pretty inconsistent about what's a string and what's an expression. Jayson provided a good list of examples of when to use (or not use) #s.
At the risk of sounding like a wise-guy, a rule of thumb is: use # around variables or expressions only when not doing so doesn't yield the correct result. Or: if you don't need them, don't use them.
I like Jayson's answer though.
Let's start by assuming you aren't talking about cfoutput tags, cause then the answer is always, elsewhere in your code, if you are inside of quotation marks, then need to use # symbols if it's possible to actually type the value that is going to be used...so if you are in a cfloop tag setting the 'to' attribute, you could easily type 6, but if you want to use a variable you need to use the # symbols. Now if you are in a cfloop tag setting the query parameter, there is no way you could actually type the query into that attribute, there is no way to type a query, so no # symbols are needed.
Likewise in a cfdump tag, you can dump static text, so if you want to dump the contents of a variable, then you will need to use a # symbol. This problem is generally self-correcting, but I feel your pain, your students are probably frustrated that there is no "ALWAYS USE THEM" or "NEVER USE THEM" approach...sadly this isn't the case, the only thing that is true, is only one way inside of quotation marks is going to be correct. So if it isn't working look at it hard and long and think to yourself: "Could I type that value out instead of using the value contained in that variable?" If the answer is no, then the # symbols won't be needed, otherwise get your # character foo on.