Variable memory space sharing? - coldfusion

In a loop, the following appends par.fval to this.filters as well as whatever par.T starts as:
<cfset par.T = this.filters />
<cfset ArrayAppend(par.T[this.name],par.fval)/>
Provided that all variables are defined initially and par.fval changes with each iteration:
<cfloop ...>
<cfdump var="#this.filters#"/>
<cfset par.T = this.filters />
<cfset ArrayAppend(par.T[this.name],par.fval)/>
</cfloop>
Will dump this.filters for each iteration with the last value of par.fval appended.
I am trying to figure out why as I would have assumed ColdFusion would overwrite par.T as an entirely new variable.
Narrowing it down to remove logic:
filtersA:
<cfdump var="#this.filters#">
<cfset par.T = #this.filters# />
<cfset ArrayAppend(par.T[this.name],par.fval)/>
filtersB:
<cfdump var="#this.filters#">
Will show that par.fval has been appended to this.filters across that append statement. So for whatever reason, par.T appears to not become its own variable instead being a reference to the memory space of the this.

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

Convert name value pairs to struct

It's been awhile since I've written this type of ColdFusion code, hence the question.
I am returning values from a .NET webservice into ColdFusion. The webservice returns an array of strings. The following code...
<cfoutput>
<cfset xArrayItems=#GetRequestedUserSettings.settingValues.getString()#>
<cfset xLen=ArrayLen(GetRequestedUserSettings.settingValues.getString())>
<cfloop index="x" from=1 to="#xLen#">
#xArrayItems[x]#<br />
</cfloop>
</cfoutput>
results in the following output ...
maxsize=50
isdomainadmin=False
seenwelcome=False
I want to put those name/value pairs into a meaningful structure so that I can reference them farther down in the code. I actually need to pass them in as a cfinvokearguments for the next webservice call.
Could someone please be kind enough to remind me how to do this in CF8? Most of what I am finding refers to newer versions.
I ended up with something quite similar to what #Henry you provided.
<cfset UserSettings = structNew()>
<cfset xArrayItems= GetRequestedUserSettings.settingValues.getString()>
<cfset xLen=ArrayLen(GetRequestedUserSettings.settingValues.getString())>
<cfloop index="x" from=1 to="#xLen#">
<cfset varName = ListGetAt(xArrayItems[x], 1, "=")>
<cfset varValue = ListGetAt(xArrayItems[x], 2, "=")>
<cfset "UserSettings.#varname#" = varValue>
</cfloop>
Not sure if an Array or a Struct is a better solution, but the both work in the end.
Slightly more readable version I can come up with that will work with CF8:
<cfset UserSettings = {}>
<cfset xArrayItems = GetRequestedUserSettings.settingValues.getString()>
<cfloop array="#xArrayItems#" index="item">
<cfset varname = ListFirst(item, "=")>
<cfset varvalue = ListRest(item, "=")>
<cfset UserSettings[varname] = varvalue>
</cfloop>

Checking for key existence in structure

I have a variable named #cfData# which contains an array of structures. As it's clear from the image, for 1st structure array there are 6 keys and for 2nd, only two keys,viz date and open.
If I run a common loop, to go through each and every key, I will get an error at second array element. So the following only works when all the keys are present in the structure:
<cfset blockedtotal = 0 />
<cfset bouncetotal = 0 />
<cfset blocked = 0/>
<cfset datetotal = 0 />
<cfloop array = #cfData# index = "i">
<cfset blockedtotal += i.blocked />
<cfset bouncetotal += i.bounce />
</cfloop>
After reading online, I got an idea of using StructKeyExists where I think I can proceed in the following way:
<cfif structKeyExists(cfData,"bounce")>
<cfoutput>Bounce:#cfData.bounce#"/></cfoutput>
<cfelse>
<cfoutput> Bounce : [none]<br/></cfoutput>
</cfif>
But I am wondering, where exactly should I insert the above code inside the cfloop? Please advise if my approach is wrong.
Update:
Thanks guys. I got it running by using the following code based on the answers and it's running fine:
<cfloop array="#cfData#" index="i">
<cfif structKeyExists(i, "date")>
<cfset counter++>
<cfoutput>#counter#</cfoutput> Date is: <cfoutput> #i.date#</cfoutput> <br/>
</cfif>
</cfloop>
you don't need a "common loop". You can loop through each struct with
<cfloop array="#cfData#" index="i">
<cfloop collection="#i#" item="key">
struct with key '#key#' has data: #i[key]#
</cfloop>
</cfloop>
Of, if you need to decide if the struct has certain key, do something:
<cfloop array="#cfData#" index="i">
<cfif structKeyExists(i, "someKey")>
<cfset counter++>
</cfif>
</cfloop>

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>

How To Loop Through Two Lists?

I need to join the output of two separate lists together to output in a CFMAIL, and I'm wondering what the best way to approach this is.
I have two form fields: first_name and last_name
The fields have up to 5 names in each. I need to loop through those names and join the first and last names, then output them to unordered list. I am having trouble visualizing what the right approach to accomplish this is.
Can someone suggest a method in CFML (I don't know CFSCRIPT very well).
Thanks!
EDIT: I should have added that both fields will always have the exact same number of entries. Thanks to all that answered -- proof that there are a lot of ways to skin a cat :)
I would do something like
<cfloop from="1" to="#ListLen(firstnames)#" index="i">
#ListGetAt(firstnames,i)# #ListGetAt(lastnames,i)#<br>
</cfloop>
If this were a list of 5000 you would be better off putting it in a structure or an array, but for a list of ~5 this should be sufficient.
I think this would be the easiest way to accomplish this.
<!--- Create a names container --->
<cfset names = "<ul>">
<!--- Fill some dummy containers --->
<cfset first = "thomas,henry,philip,john,rony">
<cfset last = "smith,baker,crowe,ryan,jones">
<!--- Loop through the lists and append them to the container string --->
<cfloop index="name" to="#listLen(first)#" from="1">
<cfset names &= "<li>" & ListGetAt(first,name) & " " & ListGetAt(last,name) & "</li>">
</cfloop>
<cfset names &= "</ul>">
<cfoutput>#names#</cfoutput>
I would add in a check to make sure that your list values exists at each index, otherwise you will get errors. I would also add in a check to loop through whichever list is greater so that you get all values just in case someone doesn't enter exactly 5 in both:
<Cfset firstnames="Matt,Ian,Brandon,Sam,Tom">
<cfset lastnames="Jones,Smith,Weiss">
<!--- SEE WHICH LIST IS LONGER AND SET THAT AS THE ONE THAT WE WILL USE FOR THE LOOP --->
<cfif ListLen(firstnames) gte ListLen(lastnames)>
<cfset primary=firstnames>
<cfelse>
<cfset primary=lastnames>
</cfif>
<cfset myOutput="<ul>">
<cfloop from="1" to="#ListLen(primary)#" index="i">
<Cfset myOutput &= "<li>">
<cfif ListLen(firstnames) gte i>
<cfset myOutput &= ListGetAt(firstnames,i)>
</cfif>
<cfif ListLen(lastnames) gte i>
<cfset myOutput &= " " & ListGetAt(lastnames,i)>
</cfif>
<Cfset myOutput &= "</li>">
</cfloop>
<Cfset myOutput &= "</ul>">
<cfoutput>#myOutput#</cfoutput>
You could use the "list" attribute with CFLOOP although it means combining list functions within the output. Here is an example though of how it could be done and it makes the assumption the two lists will always have the same lengths. If these names are keyed in by users then I might be afraid of if they put in a comma since that would throw things off with any sort of looping.
<cfset lstFirstNames = "John,Bob,Tom,Jeff" />
<cfset lstLastNames = "Smith,Doe,Rodriguez,Horan" />
<cfloop list="#Variables.lstFirstNames#" index="FirstName" />
#FirstName# #ListGetAt(Variables.LastNames, ListFind(Variables.lstFirstNames, FirstName))#<br />
</cfloop>
try:
<cfset lstFirstNames = "John,Bob,Tom,Jeff" />
<cfset lstLastNames = "Smith,Doe,Rodriguez,Horan" />
<cfloop list="#Variables.lstFirstNames#" index="FirstName">
<cfoutput>#FirstName# #ListGetAt(Variables.lstLastNames, ListFind(Variables.lstFirstNames, FirstName))#</cfoutput><br />
</cfloop>