ColdFusion - Collection loop in cfscript - coldfusion

I have some tag based syntax that works in Railo.
<cfloop collection="#myArray#" item="j" index="i"></cfloop>
The above allows me to access the index 'i' and the item itself, 'j'.
I want to do the same in cfscript, so I used:
for ( i in myArray) {}
However, 'i' gives me the item...how can I access the index value?
As a work-around, I have had to manually count an index like so:
j = 1;
for ( i in myArray) {
j++;
}
But this feels dirty. Does the for in syntax of cfscript allow for a true alternative to cfloop's collection?
I have tried Google searching all of this but never get any decent result. Is there a way to rewrite my for in loop to allow me access to the index too?
Thanks,
Mikey.

It's not possible in ColdFusion, I'm afraid, other than the work-around you are currently using, or just using an indexed for loop.
However in Railo, there is this (rather awful tag/script hybrid syntax):
<cfscript>
loop array=[5,4,3,2,1] index="i" item="v" {
writeOutput("[#i#][#v#]<br>");
}
</cfscript>
So basically it's the <cfloop> without the angle brackets.

In CF 10 and Railo 4, you could use the Underscore.cfc library.
_ = new Underscore();// instantiate the library
_.each(myArray, function(item, index) {
// code here
});
Although personally, I'd rather use one of the other functional methods, such a map or reduce, depending on what you're trying to do.
Note: I wrote Underscore.cfc

You can use:
<cfscript>
for (key in struct) {
writeOutput("#key# = #struct[key]# <br>");
}
</cfscript>
or
<cfoutput>
<cfloop collection="#params#" item="key" >
#key# = #params[key]#
</cfloop>
</cfoutput>
Remember to set "this.serialization.preservecaseforstructkey = true" in the Applicacion.cfc

Related

In ColdFusion, what is the difference between setting a function to a variable and calling a function in hashtags?

I wrote the following function:
<cffunction name="check_session_valid" returntype="boolean">
<cfif NOT StructKeyExists(session,"username") OR (len(session.username) EQ 0)>
<script>location.href = 'logout.cfm'</script>
<cfabort>
</cfif>
<cfset session.myApp_start = now()>
<cfreturn true>
</cffunction>
In my .cfm page, I can call that function using
<cfset session_valid = application.lib.check_session_valid()>
OR
#application.lib.check_session_valid()#
What's the difference? Best practice?
Since you asked about best practice, which is a matter of opinion, I think you can improve your function by having it returning either true or false depending on whether or not session.username exists and has a length greater than 0. Then you can use it like this:
<cfif application.lib.check_session_valid()>
code for this condition
<cfelse>
<cflocation href = "logout.cfm">
<!--- note that cfabort is not necessary --->
<cfif>
Regarding your specific question, I think the extra variable, session_valid, is a waste of typing. However, that is simply my opinion.
Not related to your question, I found it curious that you would direct users to a page called logout.cfm. Often users are directed to a page that allows them to log in.
To be honest, both are valid and both would be considered best practice depending on what you are trying to do.
My rule of thumb is if I will need to use the result of a function call more than once, I will set it to a variable
myResult = application.lib.check_session_valid();
If I will only need to use the variable once I would do what Dan mentioned
if( application.lib.check_session_valid() ){
// Do stuff
}
The difference between the examples you showed are
<cfset session_valid = application.lib.check_session_valid()>
This will set the variable named session_valid to whatever is returned from the call to check_session_valid().
#application.lib.check_session_valid()#
This will, in .cfm pages, simply render the value returned from the call to check_session_valid() assuming it is inside of a <cfoutput> tag. There are other places this would also render the value, such as inside a <cfsavecontent>.

Trying to find absolute string using REFind

We have list of reserved keyword that we don't want our clients to be able to use in out system. So we perform a search using REFind.
Here is the code:
<cfset reservedKeywords = "stop,end,quit,cancel,help,test">
<cfset foundArray = REFind("(?i)(" & ListChangeDelims(reservedKeywords, "|") & ")"
, form.keyword, 1, true)>
<cfif foundArray.pos[1] gt 0>
<cfoutput>
<script language="JavaScript">
alert('Keyword "#mid(form.keyword, foundArray.pos[1], foundArray.len[1])#" has been reserved.');
history.go(-1);
</script>
</cfoutput>
<cfabort>
</cfif>
So everything works great.... but we do run into a problem when a keyword is searched that has one of the reserved word IN the keyword. So if "Blended" is submitted, it will be flagged as having the reserved word "end".
Is there a way to perform an absolute search where it takes into account the whole keyword?
I've been trying to edit and play around with the code but just can't get it to work.
Any suggestions would be greatly appreciated.
Thank you!
Use listFindNoCase() instead of REFind().
The way you are currently checking is if one of the elements in the list matches form.keyword - which is why 'blended' gets tagged as 'reserved' when it shouldn't - however, you should be checking if form.keyword matches any items in the list - a subtle, but important, distinction.
reservedWords = "stop,end,quit,cancel,help,test";
reservedWordUsed = listFindNoCase( reservedWords, form.keyword );
if( reserveWordUsed ){
// do the JS stuff here
}

creating array of structs troubling

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.

ColdFusion, Setting a POST variable

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?

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.