I have a very large struct and instead of key unfortunately I have only values available.
Is there a way to switch keys and values of struct or searching for a key by passing its value?
(I am using someone else's code and can't change it.)
Use function StructFindValue to find the struct key when you only know the value.
It returns an array of matches (but limited to one match by default), each array element containing a struct with three keys:
Key - the name of the key (the thing you're after);
Path - the full path (for nested structs);
Owner - the struct the value was obtained from.
Usage example:
Find keys pointing at the localhost IP address in the CGI scope (scopes are structs too):
<cfset Result = StructFindValue( CGI , '127.0.0.1' ) />
<cfdump var=#Result[1].Key# />
Outputs remote_addr - the first value found.
Note that the ordering of structs is not guaranteed - the first value may be different in different situations.
To get all results, simply specify the scope parameter as all:
<cfset Result = StructFindValue( CGI , '127.0.0.1' , 'all' ) />
<cfloop index=i from=1 to=#ArrayLen(Result)#>
<cfdump var=#Result[i].Key# />
</cfloop>
Outputs remote_addr remote_host local_addr.
Related
Consider the following code:
<cfset local.quiz = getQuiz(param1,param2) />
<!--- returns a struct that has a key called unlock_at --->
<cfset quiz.unlock_at = (quiz.unlock_at EQ '') ? '' : DateConvert('utc2Local',createODBCDateTime(ISOToDateTime(quiz.unlock_at))) />
This is how I origionally wrote my code. When I called the page that ran this code multiple times, the value of quiz.unlock_at was changing in 6 hour increments (the amount of adjustment in the function). When I say increments I mean first page load the time was say 12:00. The next page load 6:00, the next page load 00:00 and so on. The physical time was changing each time. I changed the code to this:
<cfset local.unlock_at = (quiz.unlock_at EQ '') ? '' : DateConvert('utc2Local',createODBCDateTime(ISOToDateTime('#quiz.unlock_at#'))) />
The first thing is I'm storing the value in a local variable and no longer updating the existing struct. The second thing is I am passing in the date to the function as a string and not as the variable. I wasn't sure how it was getting altered. In either case:
How the heck was this changing between requests. I didn't think these variables lives between page requests. All of this code is inside a function that lives inside a CFC. What of any of that data is persistent. I called the page a few times and watched the dates change then opened a NEW browser and the data was altered too. How is that possible?
Is this a scoping issue, a data persistence issue with components? Threw me for a loop and I still and not sure what happened and until I do I'm afraid I may have other issues.
I figured it out. Grrr...
The function getQuiz created a struct and then cached it. The reason being is that it's an API call and I don't want to have to run an API call every time for data that doesn't change that often. So what I was doing was:
<cffunction name="getQuiz">
<cfset local.variable = {} />
<!--- Load Sturct Data --->
<!--- CachePut the variable --->
<cfreturn variable />
</cffunction>
<cfset quiz = getQuiz() />
<cfset quiz.unlock_at = 1 />
<!--- At this point I have actually edited a direct reference to the cached variable quiz even though I tried locally scoping it etc.
So, apparently returning a struct is the same as passing a struct into a function, i.e. it passes the struct by reference not by value. For some reason I thought returning a struct didn't return it as a reference, but it makes sense because that is the way CF passes values between functions.
I am trying to create an array of structures, in my Application.cfm file, which can then be appended to in further pages. I am following the EasyCFM tutorial #173 by Charlie. I am using it this way:
<cfset session.box_status = arrayNew(1) />
<cfset session.box_status[1] = structNew() />
<cfset session.box_status[1].partner_id = '0' />
<cfset session.box_status[1].partner_username = '' />
<cfset session.box_status[1].status = '0' />
In my page, I am appending to the structure like so:
<cfloop from="1" to="#arrayLen(session.box_status)#" index="i">
<cfset session.box_status[i].partner_id = ArrayAppend(i,FORM.partner_id) />
<cfset session.box_status[i].partner_username = ArrayAppend(i,FORM.partner_username) />
<cfset session.box_status[i].status = ArrayAppend(i,FORM.box_status) />
</cfloop>
But am getting an error:
The web site you are accessing has experienced an unexpected error.
Please contact the website administrator.
The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request
Object of type class java.lang.Double cannot be used as an array
In addition to Scott's comments, you need to clarify what you are actually trying to achieve. The question asks about appending a new item, yet it looks as if parts of your code attempt to overwrite the existing structure values in position session.box_status[1].
If you really want to append a new structure to the array, there is no reason to loop. Simply create an empty structure:
<cfset newItem = structNew() />
... populate it with some values:
<cfset newItem.partner_id = FORM.partner_id>
... etcetera
Then append the new structure to the array. Notice, the code below does not care about the result of ArrayAppend. That is because the function modifies the array in place, and only returns true/false depending on whether the action was successful.
<cfset ArrayAppend(session.box_status, newItem)>
Update:
That said, the tutorial you are using was obviously written for an older version of CF. As #cfqueryparam pointed out, later versions support a shorthand for creating arrays and structures. Instead of using structNew(), you could simply do this:
<cfset newItem = { partner_id = FORM.partner_id, ... etectera }>
The first argument in arrayAppend() needs to be the array to which you are appending something, in your example, you are using i - which is the counter of your loop - which is a number, not an array.
Note that a common error is to pass the array name, but forget to put the pound symbols. For me, when I make the error of saying
<cfloop array="myAry" index="aryElement">
instead of the proper expression
<cfloop array="#myAry#" index="aryElement">
then the debug message java.lang.string cannot be used as an array is issued.
If there is a better way to go about this (which is quite likely), please let me know how to go about it.
I'm working on some code that is supposed to dynamically set the form variables as regular variables so that we can be lazy and not have to refer to the variable with form.somevariable name.
That part works perfectly. Until I start testing for URL conflicts in which a URL variable has the same name. For instance. . .
I have a form that passes two variables; FirstName and LastName. If I hit the page, the form shows up, I input a first and last name and click submit. The code works perfectly.
However, if I have URL variables with the same names, the code reports the url variable values instead of the form values.
Some sample values;
url.FirstName = Joe
url.LastName = Black
form.FirstName = Steve
form.LastName = White
My code that exposes the form variable will correctly find the form field names, but then when I 'evaluate' the value of the given form field, it will return the value of the URL variable of the same name rather than the form variable.
What I am really wanting (as I described briefly up above) is to have code that automatically converts client, URL and Form variables into 'regular variables' so that you don't have to write lots of extra code grabbing them later on. Frameworks like CFWHEELS and ColdBox do this by default, but at the company I work out, we aren't using any of them. I need it to expose the URL variables, but give presidence to form variables if they have the same name, because they are likely to be intended to do an update or such.
The code follows Feel free to ignore the code for the URL and client variables if you wish as they don't directly affect how the form code works, I have tested with them commented out and I get the same result. I provided all of it to give a more complete idea of what I have been toying with so far. Please note that I don't normally use 'evaluate'. There is probably a better way to go, but I don't know what it is.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++
First Name
Last Name
URL variables:
<cfloop index="i" list="#paramsurl#">
<cfset myDynVar = Evaluate(i)>
<!--- Let's output the dynamically created variable as a test --->
#i# = #myDynVar#<br />
</cfloop>
<cfoutput>
<b>Field Names:</b> #Form.FieldNames#
<p>
<b>Field Values:</b><br>
<cfloop INDEX="TheField" list="#Form.FieldNames#">
#TheField# = #Evaluate(TheField)#<br>
<cfset TheField = Evaluate(TheField)>
</cfloop>
</p>
Lets try and output the two form fields without using the "form." notation<br>
FirstName : #FirstName# <br />
LastName : #LastName#
</cfoutput>
The client variables currently available are:
<cfset nVarCounter = 1>
<cfloop list="#GetClientVariablesList()#" index="whichClientVar">
#whichClientVar# : #client[whichClientVar]#<br />
<cfset whichClientVar = Evaluate(whichClientVar)>
</cfloop>
You should always scope your variables. When you use evaluate it runs through the scope order and it pulls the values out of the url scope before it gets to the form scope
You can use associative array notation to pull the data (as seen below).
<cfoutput>
<b>Field Names:</b> #Form.FieldNames#
<p>
<b>Field Values:</b><br>
<cfloop INDEX="TheField" list="#Form.FieldNames#">
#TheField# = #form[TheField]#<br><!--- specify form scope --->
<cfset myField = structKeyExists(url,TheField) ? url.TheField : form.TheField>
</cfloop>
</p>
</cfoutput>
You can 'copy' the values from form scope and url scope into the variables scope by using structAppend().
structAppend( variables, form, true );
structAppend( variables, url, false );
In the first line, any element of the form scope is copied to the variables scope and if a variable already exists with the same name in variables scope, it will overwrite that value with the value from the form scope.
In the second line, elements form URL scope are copied to variables scope but if a variable already exists in the variables scope, it is NOT overwritten.
You can do this for ANY scope and any other ColdFusion structure. You can also reorder them so that one scope has precedence over the others.
In CF 10 or Railo 4, you could use the defaults() function of the Underscore.cfc library to succinctly accomplish what you're trying to do. Example:
// instantiate Underscore library
_ = new Underscore();
// copy values from form and url scopes into the variables scope
_.defaults(variables, form, url);
This function "fills in" any undefined values in the first struct to the values in the subsequent structs. It works from left to right, so in this example it gives precedence to values in form over the values in url.
Disclaimer: I wrote the Underscore.cfc library.
Please refer to the following Adobe documentation for order of precedence:
http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec09af4-7fdf.html
If you want to reverse this precedence for some reason you should be able to just set all of your FORM fields into the variables scope...
Maybe something like ...
<cfloop collection=#form# item="varName">
<cfset SetVariable("variables.#varName#", evaluate("FORM." & varName))>
</cfloop>
Thanks for all of the great ideas.
Following is what I ended up going with.
<cfset scopes = "url,client,form">
<cfloop list="#scopes#" index="i">
<cfloop list="#structKeyList( evaluate( i ) )#" index="j">
<cfset structInsert( VARIABLES, j, evaluate( i & '["' & j & '"]' ), true ) />
</cfloop>
</cfloop>
<cfoutput>
Lets try and output the two form fields without using the "form." notation and make sure that the URL variables are NOT over writing the Form ones<br>
FirstName : #FirstName# <br />
LastName : #LastName#<br />
</cfoutput>
<cfdump var="#VARIABLES#" abort="1" label="Combined Variables Scope stuff" />
Im editing my first ColdFusion script .... I have a form which has <input type="hidden" name="name" value="1">.
On the processing page i want to take that value and set it as a POST variable so i can send it onto another page.
I know how to do it in PHP, like so
$_POST['somename'] = $_POST['name']
How would i do that in CF?
Following the idiom in your php code, you can do something like this:
<cfset form['somename'] = form['name']>
...or, if in cfscript:
form['somename'] = form['name'];
If you're concerned about the existence of the variable, you can precede the assignment with <cfparam>:
<cfparam name="form.name" default=""><!--- assuming blank ok as default --->
<cfset form['somename'] = form['name']>
...or in script:
param name='form.name' default='';
form['somename'] = form['name'];
Of course you can also wrap the assignment in a conditional:
if( structkeyexists(form,'name') ){
form.somename = form.name; // dot notation alternative to bracket syntax
}
This all begs the question of what exactly you're trying to achieve with this approach.
The ColdFusion syntax is similar. "Post" variables are available in the system structure FORM, and "Get" variables in the system structure URL. Like in PHP, values can be accessed using associative array notation. You can also use dot notation (for valid field names)
<cfset otherVariable = FORM["variableName"] >
<cfset otherVariable = FORM.variableName >
i want to take that value and set it
as a POST variable so i can send it
onto another page.
I am not quite sure what you mean there. Typically, you do not need to reassign FORM or URL values. You simply reference the variable in your code.
<cfoutput>
Go To Other Page
</cfoutput>
You can try this by checking if the post variable is set and then storing it with scope of FORM.
<cfif isdefined ("FORM.name")>
<cfset FORM.somename="#FORM.name#">
</cfif>
How Can I get the URL parameter & Value in Coldfusion?
for Ex:-
my URL is
test.cfm?par1=val1&par2=val2&par3=val3
Is it possible to get the second parameter and its value directly?
with <cfset param='#url.par2#'> I can get value of par2,
But my parameters are dynamicically generated from other page and passed to here (par2 may be next time abc2,xyz2 etc..)
So I think better way is to get the parameter and Value in 2nd Possition(Possition dont change always).
Any Idea How can I get it ?
Thanks in advance
You can also access the url scope as a struct, so you could get:
<cfset param2 = url['param2'] />
This is useful if you might have a naming convention for a bunch of fields. Say you're collecting names and emails like so:
email1=foo#bar.com&name1=Fred&email2=xxx#yyy.com&name2=Sally
You could write something like:
<cfloop condition="someCondition">
<cfset email = url['email' & i] />
<cfset name = url['name' & i] />
<!--- Do something --->
<cfset i++ />
</cfloop>
<cfset Param2 = ListGetAt(CGI.QUERY_STRING,2,"&")>
Order of query string variables is not relevant, or your app shouldnt expect it to be relevant. I think your best bet is to have another variable which is a list of the variables in the order. Like so:
test.cfm?par1=val1&par2=val2&par3=val3&list=var1,var2,var3
Notice the presence of the new variable "list".
So you first grab the value of "list" and then takes it 2nd entry "var2" and reference that in the URL scope. You could easily abstract all of this so the names of the variables themselves dont matter. Good error handling will be necessary to guard against missing expectations.
to get the list of params you can use structKeyList(url) or structKeyArray(url) then access those parameters through the url scope like #url['par1']#
<cfset params = structKeyList(url) />
<cfdump label="parameters" var="#params#" />
<cfloop index="ix" list="#params#">
<cfoutput><div>#ix# = #url[ix]#</div></cfoutput>
</cfloop>
as others have mentioned, you really shouldn't rely on the order of parameters
<cfscript>
par2=getToken(cgi.query_string,2,"&"); // contains "par2=val2"
par2name=getToken(par2,1,"="); // contains "par2"
par2value=urlDecode(getToken(par2,2,"=")); // contains "val2"
</cfscript>
You could also use the listGetAt function, which is basically equivalent to getToken, with slightly different syntax.