Check string contains a list element using Coldfusion - coldfusion

I know that listcontains() is used to check the list contains a specified string. And is there any way to check a string contains a list element using Coldfusion?

Like this:
<cfloop from="1" to="#ListLen(yourList)#" index="elemementNumber">
<cfif yourString contains ListGetAt(elemementNumber)>
yes
<cfelse>
no
</cfif>
</cfloop>

I think what you need is ListFindNoCase() or ListFind() depending on the obvious.

Related

Coldfusion Complex Construct

I am trying to construct a coldfusion conditional statement that looks for incremental form ID checkboxes to have been selected. All checkboxes are defined as Component[component number]. I have established a loop that is looking for a URL variable that is different for every form that calls on the condition as seen below. The issue I am having is that I recieve an error when executing that tells me "Complex constructs are not supported with function parameterexists."
Clearly it has to do with the dynamic nature of the parameterexists statement, but I do not fully know what this means. Can anyone explain this and also offer a solution? I am fairly new to coldfusion and coding, so take it easy on me.
<cfloop from="1" to="#URL.loopcounter#" index="loopvar">
<cfif parameterexists(Form.Component#loopvar#)>
INSERT INTO Results (MP_Barcode, Reworked, Reworked_By)
VALUES ('#Form.MontaplastBarcode#', 'YES', '#URL.BadgeNumber#')
</cfloop>
<cfoutput>
<p class="success">YOUR REWORK HAS BEEN SUBMITTED SUCCESSFULLY.</p>
</cfoutput>
<cfelse>
<p class="error">NO REWORK WAS SUBMITTED. NO COMPONENTS SELECTED.</p>
</cfif>
Depending on the form that calls on this action, the URL loopcounter variable could range from 1 to 50.
To answer the question, there are several ColdFusion functions that won't allow you to create a dynamic name before the function evaluates it. parameterExists() was one of those. Both isDefined() and structKeyExists() will allow dynamic variables. So will the member function of structKeyExists() > structName.keyExists("theKey").
Again, if you are new to ColdFusion, I'd simply pretend you never saw parameterExists(). I believe it has been listed as "deprecated" since CF 4.5 or somewhere around there. That's almost 20 years ago. That function has actually become somewhat of a joke about how Adobe never really throws away their trash.
As I pointed out above, I'd get rid of it completely and go with structKeyExists(). I also don't know what your whole page is doing, but with the code you provided, I'd change it to something like this:
<cfloop from="1" to="#url.loopcounter#" index="loopvar">
<cfoutput>
<cfif structKeyExists(form,"Component#loopvar#")>
<!--- SANITIZE INPUTS --->
<cfset inMontplastBarcode = sanitizingFunction(FORM.MontaplastBarcode)>
<cfset inBadgeNumber = sanitizingFunction(URL.BadgeNumber)>
<!--- Now use sanitized inputs in query with queryparams --->
<cfquery name="InsertStuff" datasource="myds">
INSERT INTO Results (MP_Barcode, Reworked, Reworked_By)
VALUES (
<cfqueryparam value="#inMontaplastBarcode#" cfsqltype="cf_sql_varchar" maxlength="50">
, 'YES'
, <cfqueryparam value="#inBadgeNumber#" cfsqltype="cf_sql_varchar" maxlength="20">
)
</cfquery>
</cfif>
</cfoutput>
</cfloop>
In your database, Reworked should be a boolean datatype. It appears that it may be a 'Yes' or 'No' string. A true boolean will be a) smaller and b) easier to validate. In the cfqueryparams, if you are using a cf_sql_varchar datatype, make sure you set an appropriate max length. You'll need to look at the available CF datatypes and see how they match up to your database datatypes. (Also see https://cfdocs.org/cfqueryparam)
For your sanitizingFunction() that you'll use to sanitize your input variables, you'll want to write a function that will follow through the steps to clean up your variables to strip out unsafe characters or other things you don't want. That is an entirely different, extremely large topic all on its own.
In your form, name your checkboxes simpler. Like reworked01 through reworked50.
On the action page use cfparam to default them to zero (since html forms don't post unchecked boxes):
<cfloop from="1" to="#url.loopCounter#" index="i">
<cfparam name="form.reworked#numberFormat(i, 00)#" default="0">
</cfloop>
Then instead of fumbling with whether or not a variable exists, you can instead look for the value:
<cfloop from="1" to="#url.loopCounter#" index="i">
<cfif evaluate("form.reworked"&i) eq 1>
<!--- some logic here --->
<cfelse>
<!--- some other logic here --->
</cfif>
</cfloop>

CFSPREADSHEET read in Excel file to query can't access columns in result

I'm trying to automate processing of spreadsheets using ColdFusion 10 and CFSpreadSheet. So far I can read the file in and dump the query object without any issue.
<cfspreadsheet action="read" src="#theFile#" query="qData" headerrow="1" columns="1,8,9,11,33"/>
<cfdump var="#qData#"/>
The issue comes when I try to work with the data. If I do something like:
<cfoutput query="qData" maxrows="#qData.RecordCount#">
#qData.GROUP#<br />
#qData.DOB#<br />
</cfoutput>
I immediately get an error: "Element GROUP is undefined in QDATA."
If I dump the qData.ColumnList I get a column list:
BTBN(002),DOB ,GROUP ,MEMBER/DEPENDENT NAME ,REL
Is it the spaces and ( )s messing it up? If so, how can I get rid of those and update the column names either when reading in the file or immediately thereafter?
I initially thought it may be due to "group" being a reserved SQL keyword. (Don't try using that columnname in a query-of-queries.)
Instead of sanitizing the first row values for known values and resaving the file, you should loop through and use isValid("variablename", ColumnName) to determine if the column name is valid and then use the RenameColumn UDF to rename it. We prefer this method as it's critical that we don't modify the client's original Excel file (especially since Adobe ColdFusion has some bugs when writing files and will likely mess up other worksheets and/or formatting within the file.)
An easy way to remove illegal characters is to use rereplace:
ReReplace(NewColumnName, "[^a-zA-Z0-9!]", "", "ALL")
But you'll also need to ensure that the new column name isn't empty, starts with a letter and isn't already used for another column. If you are expecting the columns to be in a certain order, you could simply safely rename them "col_1" (or use numbering as a default fallback for any non-unique and/or illegal column names.)
Here's the RenameColumn UDF taken from this 2011 blog post:
http://www.neiland.net/blog/article/using-java-to-rename-columns-in-a-coldfusion-query-object/
<cffunction name="renameColumn" access="public" output="false" returntype="query" hint="Uses java to rename a given query object column">
<cfargument name="queryObj" required="true" type="query">
<cfargument name="oldColName" required="true" type="string">
<cfargument name="newColName" required="true" type="string">
<!--- Get an array of the current column names --->
<cfset var colNameArray = queryObj.getColumnNames()>
<cfset var i = 0>
<!--- Loop through the name array and try match the current column name with the target col name--->
<cfif arrayLen(colNameArray)>
<cfloop from="1" to="#arrayLen(colNameArray)#" index="i">
<!--- If we find the target col name change to the new name --->
<cfif compareNoCase(colNameArray[i],arguments.oldColName) EQ 0>
<cfset colNameArray[i] = arguments.newColName>
</cfif>
</cfloop>
</cfif>
<!--- Update the column names with the updated name array --->
<cfset queryObj.setColumnNames(colNameArray)>
<cfreturn queryObj />
</cffunction>
(In case folks do not read the comments ...)
The parenthesis and slashes will be problematic as they do not conform to the standard variable name rules. The simplest option is to use the "columnNames" attribute to specify valid column names instead. (Also, nothing to do with your question, but if you want to exclude the header row, use excludeHeaderRow="true")
<cfspreadsheet action="read" src="c:\path\file.xlsx"
query="qData"
columnNames="BTBN_002,DOB,GROUP_NAME,MEMBER_DEPEND_NAME,REL"
excludeHeaderRow="true"
headerrow="1"
... />
In most cases, you can also access invalid column names using associative array notation. However, using the "columnNames" attribute is simpler/cleaner IMO.
<cfoutput query="qData" maxrows="#qData.RecordCount#">
#qData["BTBN(002)"][currentRow]#<br />
....
</cfoutput>
SOLUTION - there were multiple spaces in the column names and ColdFusion does not tolerate that. This could probably be done better with a regex, and I'll work on that next but here's the quick and dirty solution.
<cfset colNameArray = qData.getColumnNames() />
<cfloop from="1" to="#arrayLen(colNameArray)#" index="i">
<cfset colNameArray[i] = colNameArray[i].replace(' ','') />
<cfset colNameArray[i] = colNameArray[i].replace('(','') />
<cfset colNameArray[i] = colNameArray[i].replace(')','') />
<cfset colNameArray[i] = colNameArray[i].replace('/','') />
</cfloop>
<cfset qData.setColumnNames(colNameArray) />

Use FindNoCase to find url string

I've search around and find some valuable info. about FindNoCase but I have not yet found the answer to this specific question.
I'm using FindNoCase to find "/us/" in the url and then process some code. This works fine for one country site.
<cfif FindNoCase("/us/",#cgi.SCRIPT_NAME#)>
Process some code here.
</cfif>
My question is: Is there a way to find the piece of the url, "/xx/", for multiple country sites and process the same code between the cfif tags? For example "/us/", "/ca/", "/mx/", etc.
Hopes this makes sense.
If searching for multiple different codes among a string, I'd use REFindNoCase. Make a regex like so (/us/)|(/ca/) which would look for /us/ or /ca/ and per the documentation (http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7e99.html) you can return the sub-expressions and reference them.
No looping required.
REFindNoCase("(/us/)|(/ca/)", URL, 1, true)
To reference the strings you would do:
<cfset URL = 'domaim.com/page/us/' />
<cfset match = REFindNoCase("(/us/)|(/ca/)", URL, 1, true) />
<cfif arrayLen(match) GT 0>
<cfset value = Mid(URL,match.pos[1],match.len[1]) />
<cfswitch expression="#value#">
<cfcase value="/us/">
<!--- Do something for US match --->
</cfcase>
<cfcase value="/ca/">
<!--- Do something for CA match --->
</cfcase>
<!--- ETC --->
</cfswitch>
<cfelse>
<!--- Do something if no match found --->
</cfif>
In this case, value would equal /us/. Or it should anyway. I'm writing all of this here and not actually testing on my server. You'd have to adjust this if you want to cover multiple matches in a string.
If I'm understanding your question correctly, you want to process the SAME code for several countries, if detected in the URL. You could just create a database of country codes you want to process a certain way (CountryTableXYZ), query it, then loop through that query so it'll search the URL for each entry you have in the table.
<cfloop query="CountryQuery">
<cfif FindNoCase("#countryCode#",#cgi.SCRIPT_NAME#)>
Process some code here.
</cfif>
</cfloop>
At least that's how I interpret it. If I'm not correct please let me know or clarify your initial post.

Check values in a list are all identical

Ok here's a thing, I have a form which when submitted can only update a table when a particular checkbox form variable values are identical, if one is different then it should proceed to update the table. So basically the user is selecting different rows and hitting the submit button.
So for example a good submission would be
form.claimid = 12,12,12,12,12
a bad submission would be
form.claimid = 1,5,77,33,4,
I'm not sure how to check in a list if all the values in the form.claimid list are identical or not?
I would appreciate any ideas on this.
You can also use CF's native ListRemoveDuplicates() if the version is sufficient (CF10, Railo/Lucee 4)
https://wikidocs.adobe.com/wiki/display/coldfusionen/ListRemoveDuplicates
<cfif ListLen(ListRemoveDuplicates(mylist)) eq 1>
I'll leave this as an alternate means for older CFs.
I'm not sure how to check in a list if all the values in the form.claimid list are identical or not?
There are a couple ways, most of which involve looping, but I'm going to show you a regular expression that can do this. We're going to get the first value using ListFirst, and use a ReReplace on that value to see if every other value matches.
I use a loop here but only as a demonstration.
<cfoutput>
<cfset mylist = "11,22,33,44|44,44,33,44|557,557,557">
<cfloop list="#mylist#" index="m" delimiters="|">
<cfset matchele = listfirst(m)>
<cfset eradicate = rereplace(m,"(?:(?:^|,)#matchele#(?=,|$))+","","ALL")>
"#m#", "#matchele#", "#eradicate#"<br />
</cfloop>
</cfoutput>
If you're stuck on CF 9 or lower, you could loop over the list and put the elements into a struct:
<cfset list_coll = structNew() />
<cfloop list="#mylist#" index="myitem">
<cfset list_coll[myitem] = "dummy" />
</cfloop>
<cfif structCount(list_coll) EQ 1>
<!--- This is good --->
<cfelse>
<!--- This is bad --->
</cfif>
The reason this works is that structs can't have duplicate keys.

coldfusion query loop not cooperating

I'm trying to create a function to create csv files from queries. After I run the query, I'm looping through it and appending each row's fields to a StringBuffer object. To that end, I'm putting the column names into an array:
<cfset indexes = #ListToArray(Arguments.header)# />
where the argument is currently a string like:
"col1, col2, col3...."
I've verified that both the query and the array are what they should be by dumping.
The trouble comes when looping through the query results. Given:
<cfset indexes_length = #ArrayLen(indexes)# />
<cfloop query="query_for_csv">
<cfloop index="i" from="1" to="#indexes_length#">
<cfset attr = #indexes[i]# />
<cfset sbOutput.Append(
"#query_for_csv[attr][query_for_csv.CurrentRow]#") />
</cfloop>
</cfloop>
Only the first value of the first row is output before I get the error message:
[Table (rows 10 columns col1, col2, col3):
[col1: coldfusion.sql.QueryColumn#6f731eba]
[col2: coldfusion.sql.QueryColumn#6ee67e7f]
[col3: coldfusion.sql.QueryColumn#5c6647cb]
is not indexable by col2
If I replace the variable #attr# with the literal "col2":
#query_for_csv['col2'][query_for_csv.CurrentRow]#
then the loop sails through with no problem, and spits out all the values indexed by 'col2'. Any ideas?
I would guess it's the spaces in your header list that is the problem, so probably this would work:
<cfset attr = trim(indexes[i]) />
However, since you're not using them, you probably don't need that and can just do this...
<cfloop query="QueryName">
<cfloop index="CurCol" list=#QueryName.ColumnList# >
<cfset sbOutput.Append(
QueryName[CurCol][QueryName.CurrentRow]
)/>
</cfloop>
</cfloop>
p.s.
You'll note here that there's only one pair of hashes - there only needs to be one pair in your original code snippets too (in the to attribute) - the rest are unnecessary noise.
As has already been said before, try to avoid spaces before or after a list element.
In case you want to compare notes, check out the approach Ben Nadel chose to implement such a Query2CSV converter: http://www.bennadel.com/blog/1239-Updated-Converting-A-ColdFusion-Query-To-CSV-Using-QueryToCSV-.htm