Related
I have an array "varray" which needs to be split into four structures. The first of each four elements should be in structure 1, the second in structure 2, etc. I have some working code to do this, but it feels to me like there should be a less cumbersome way. Here is the code:
<cfset xord = StructNew()>
<cfset xsort = StructNew()>
<cfset xsel = StructNew()>
<cfset xmer = StructNew()>
<cfloop from = '1' to = "#ArrayLen(varray)#" index = 'i'>
<cfset fieldname = farray[i]> <!---farray previously defined --->
<cfset val = varray[i]> <!---varray previously defined --->
<cfset j = i%4>
<cfif j EQ 1>
<cfset xord[fieldname] = val>
<cfselseif j EQ 2>
<cfset xsort[fieldname]= val>
<cfelseif j EQ 3>
<cfset xsel[fieldname] = val>
<cfelseif j EQ 0>
<cfset xmer[fieldname] = val>
</cfif>
</cfloop>
Can anyone suggest a better way to do this?
It's been ages i did some CF, but a tag based approach by making use of local scope:
<cfset keys = ['xord', 'xsort', 'xsel', 'xmer'] />
<cfset farray = ['f1','f2','f3','f4','f5','f6','f7','f8']>
<cfset varray = ['v1','v2','v3','v4','v5','v6','v7','v8']>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset local[keys[i%4+1]][farray[i]] = varray[i]>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
Now you have xord, xsort, xsel and xmer filled with the right key-value pairs within your local scope.
How about cfscript?
<cfscript>
function groupByOp(values, fieldnames) {
var ops = ['mer', 'ord', 'sort', 'sel'];
var byOp = {};
arrayEach(values, function (val, i) {
byOp["x#ops[i % 4 + 1]#"][fieldnames[i]] = val;
});
return byOp;
}
</cfscript>
This makes use of the fact that CF will automagically create structs when you mention a non-existing member.
Test:
<cfset v = ListToArray('1,2,3,4,5,6')>
<cfset f = ListToArray('a,b,c,d,e,f')>
<cfoutput>
<pre>#SerializeJSON(groupByOp(v, f))#</pre>
</cfoutput>
outputs
{
"xsel": {
"c": "3"
},
"xord": {
"e": "5",
"a": "1"
},
"xsort": {
"b": "2",
"f": "6"
},
"xmer": {
"d": "4"
}
}
(You didn't mention your version, so I don't know if you have access to newer functions like array each(). Keep in mind there's slicker options in newer versions)
Instead of creating separate variables, create a single structure containing the 4 variables, and an array of names. Then use the array and MOD to populate the substructures. Note, the example below creates the subsubstructures up front to ensure they always exist - even if the field/value arrays are empty or contain less than 4 elements.
TryCF.com Example
<cfset farray = ["A","B","C","D","E","F","G","Q","T"]>
<cfset varray = ["11","22","33","RR","55","NN","77","68","46"]>
<cfset data = {xOrd={},xSort={},xSel={},xMer={}}>
<cfset names = ["xOrd","xSort","xSel","xMer"]>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset fieldName = farray[i]>
<cfset fieldValue = varray[i]>
<cfset structName = names[i%4+1]>
<cfset data[structName][fieldName] = fieldValue>
</cfloop>
The substructures can be accessed through the parent, data.
<cfdump var="#data.xOrd#" label="data.xOrd">
<cfdump var="#data.xSort#" label="data.xSort">
<cfdump var="#data.xSel#" label="data.xSel">
<cfdump var="#data.xMer#" label="data.xMer">
Another couple of approaches.
I wouldn't necessarily say that these are better, but just different:
Requires field array length to match value array length
<cfset farray = ['field1','field2','field3','field4','field5']>
<cfset varray = ['apple','orange','pear','kiwi','pineapple']>
<cfset xord = {}>
<cfset xsort = {}>
<cfset xsel = {}>
<cfset xmer = {}>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset j =
((i%4 EQ 1) ? (StructInsert(xord,farray[i],varray[i])):
((i%4 EQ 2) ? (StructInsert(xsort,farray[i],varray[i])):
((i%4 EQ 3) ? (StructInsert(xsel,farray[i],varray[i])):
((i%4 EQ 0) ? (StructInsert(xmer,farray[i],varray[i])): 0))))>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
OR:
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfswitch expression="#i%4#">
<cfcase value="0">
<cfset xmer[farray[i]] = varray[i]>
</cfcase>
<cfcase value="1">
<cfset xord[farray[i]] = varray[i]>
</cfcase>
<cfcase value="2">
<cfset xsort[farray[i]] = varray[i]>
</cfcase>
<cfcase value="3">
<cfset xsel[farray[i]] = varray[i]>
</cfcase>
</cfswitch>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
Field array length does not have to match value array length
<cfset farray = ['field1','field2','field3','field4']>
<cfset varray = ['apple','orange','pear','kiwi','pineapple']>
<cfset xord = {}>
<cfset xsort = {}>
<cfset xsel = {}>
<cfset xmer = {}>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset j =
((i%4 EQ 1 AND ArrayIsDefined(farray,i)) ? (StructInsert(xord,farray[i],varray[i],true)) :
((i%4 EQ 2 AND ArrayIsDefined(farray,i)) ? (StructInsert(xsort,farray[i],varray[i],true)) :
((i%4 EQ 3 AND ArrayIsDefined(farray,i)) ? (StructInsert(xsel,farray[i],varray[i],true)) :
((i%4 EQ 0 AND ArrayIsDefined(farray,i)) ? (StructInsert(xmer,farray[i],varray[i],true)) : 0))))>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
OR:
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfswitch expression="#i%4#">
<cfcase value="0">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xmer,farray[i],varray[i],true)) : 0)>
</cfcase>
<cfcase value="1">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xord,farray[i],varray[i],true)) : 0)>
</cfcase>
<cfcase value="2">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xsort,farray[i],varray[i],true)) : 0)>
</cfcase>
<cfcase value="3">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xsel,farray[i],varray[i],true)) : 0)>
</cfcase>
</cfswitch>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
I have this function to generate slugs in Coldfusion:
<cffunction name="generateSlug" output="false" returnType="string">
<cfargument name="str">
<cfargument name="spacer" default="-">
<cfset var ret = "" />
<cfset str = lCase(trim(str)) />
<cfset str = reReplace(str, "[àáâãäå]", "a", "all") />
<cfset str = reReplace(str, "[èéêë]", "e", "all") />
<cfset str = reReplace(str, "[ìíîï]", "i", "all") />
<cfset str = reReplace(str, "[òóôö]", "o", "all") />
<cfset str = reReplace(str, "[ùúûü]", "u", "all") />
<cfset str = reReplace(str, "[ñ]", "n", "all") />
<cfset str = reReplace(str, "[^a-z0-9-]", "#spacer#", "all") />
<cfset ret = reReplace(str, "#spacer#+", "#spacer#", "all") />
<cfif left(ret, 1) eq "#spacer#">
<cfset ret = right(ret, len(ret)-1) />
</cfif>
<cfif right(ret, 1) eq "#spacer#">
<cfset ret = left(ret, len(ret)-1) />
</cfif>
<cfreturn ret />
</cffunction>
and then i am calling it using this:
<cfset stringToBeSlugged = "This is a string abcde àáâãäå èéêë ìíîï òóôö ùúûü ñ año ñññññññññññññ" />
<cfset slug = generateSlug(stringToBeSlugged) />
<cfoutput>#slug#</cfoutput>
But this is output me this slug:
this-is-a-string-abcde-a-a-a-a-a-a-e-e-e-e-i-i-i-i-o-o-o-o-u-u-u-u-n-a-no-n-n-n-n-n-n-n-n-n-n-n-n-n
it seems that all the accented characters are correctly replaced but this function is inserting a '-' after replacing them. Why?
Where is the error?
PD: i am expecting this output:
this-is-a-string-abcde-aaaaaa-eeee-iiii-oooo-uuuu-n-ano-nnnnnnnnnnnnn
Thanks.
Does this work for you? (I've adapted a similar script that we use internally.) I believe that we used this with ColdFusion 8 as we are still use it w/CF9.
<cffunction name="generateSlug" output="false" returnType="string">
<cfargument name="str" default="">
<cfargument name="spacer" default="-">
<cfset var ret = replace(arguments.str,"'", "", "all")>
<cfset ret = trim(ReReplaceNoCase(ret, "<[^>]*>", "", "ALL"))>
<cfset ret = ReplaceList(ret, "À,Á,Â,Ã,Ä,Å,Æ,È,É,Ê,Ë,Ì,Í,Î,Ï,Ð,Ñ,Ò,Ó,Ô,Õ,Ö,Ø,Ù,Ú,Û,Ü,Ý,à,á,â,ã,ä,å,æ,è,é,ê,ë,ì,í,î,ï,ñ,ò,ó,ô,õ,ö,ø,ù,ú,û,ü,ý, ,&", "A,A,A,A,A,A,AE,E,E,E,E,I,I,I,I,D,N,O,O,O,O,O,0,U,U,U,U,Y,a,a,a,a,a,a,ae,e,e,e,e,i,i,i,i,n,o,o,o,o,o,0,u,u,u,u,y, , ")>
<cfset ret = trim(rereplace(ret, "[[:punct:]]"," ","all"))>
<cfset ret = rereplace(ret, "[[:space:]]+","!","all")>
<cfset ret = ReReplace(ret, "[^a-zA-Z0-9!]", "", "ALL")>
<cfset ret = trim(rereplace(ret, "!+", arguments.Spacer, "all"))>
<cfreturn ret>
</cffunction>
<cfset stringToBeSlugged = "This is a string abcde àáâãäå èéêë ìíîï òóôö ùúûü ñ año ñññññññññññññ" />
<cfoutput>"#stringToBeSlugged# = #generateSlug(stringToBeSlugged)#</cfoutput>
Support for more International Character
If you want to widen your support for international characters, you could use ICU4J (java) and Paul Hastings' Transliterator.CFC to transliterate all of the characters and then replace any remaining spaces, dashes, slashes, etc with dashes.
https://gist.github.com/JamoCA/ec4617b066fc4bb601f620bc93bacb57
http://site.icu-project.org/download
After installing both, you can convert non-Latin characters by identifying the language id (to be converted to) and pass the string to be converted:
<cfset Transliterator = CreateObject("component","transliterator")>
<cfoutput>
<cfloop array="#TestStrings#" index="TestString">
<h3>TestString = "#TestString#"</h3>
<blockquote>
<div>CFC-1 = #Transliterator.transliterate('Latin-ASCII', TestString)#</div>
<div>CFC-2 = #Transliterator.transliterate('any-NFD; [:nonspacing mark:] any-remove; any-NFC', TestString)#</div>
</blockquote>
<hr>
</cfloop>
</cfoutput>
<h2>Available Language IDs</h2>
<cfdump var="#Transliterator.getAvailableIDs()#" label="Language IDs">
I have a txt file and would like to order all the rows according to a value in each row.
What is the best and fastest possible way to achieve this?
Below is the code that I use to compile my txt documents:
<!---CSV FILE--->
<cffile action="read" file="C:/ColdFusion10/cfusion/wwwroot/kelly2/debitorders.csv" variable="csvfile">
<cfoutput>
<!---LOOP THROUGH CSV FILE--->
<cfloop index="index" list="#csvfile#" delimiters="#chr(10)##chr(13)#">
<!---SET VALUES--->
<cfset accountholder = "#listgetAt('#index#',1)# #listgetAt('#index#',2)#">
<cfset accountholderlname = "#listgetAt('#index#',2)#">
<cfset accountnumber = "#listgetAt('#index#',3)#">
<cfset accounttype = "#listgetAt('#index#',4)#">
<cfset bankname = "#listgetAt('#index#',5)#">
<cfset branch = "#listgetAt('#index#',6)#">
<cfset amount = "#listgetAt('#index#',7)#">
<cfset date = "#listgetAt('#index#',8)#">
<!---SET INITIAL--->
<cfset initial = "#left(accountholder,1)#">
<!---SET LAST NAME--->
<cfset lname_final = "#replace("#accountholderlname#"," ","","all")#">
<!---GET AND SET ACC TYPE--->
<cfif accounttype eq "cheque">
<cfset accounttype_final = "CH">
<cfelseif accounttype eq "savings">
<cfset accounttype_final = "SAV">
<cfelseif accounttype eq "credit">
<cfset accounttype_final = "CR">
<cfelse>
<cfset accounttype_final = "OTH">
</cfif>
<!---SET AMOUNT--->
<cfset amount_final = #round(amount * 100)#>
<cfset amount_final = #NumberFormat(amount_final,"0000000")#>
<!---SET DATE--->
<cfset date_final = "#DateFormat(Date,"ddyyyymm")#">
<!---TRIM VALUES--->
<cfset initial = "#Left(initial, 1)#">
<cfset lname_final = "#Left(lname_final, 14)#">
<cfset accountnumber = "#Left(accountnumber, 13)#">
<cfset accounttype_final = "#Left(accounttype_final, 3)#">
<cfset branch = "#Left(branch, 9)#">
<cfset amount_final = "#Left(amount_final, 7)#">
<cfset date_final = "#Left(date_final, 8)#">
<!---SET STRING LENGTH FOR EACH--->
<cfset initial = "#LJustify(initial, 1)#">
<cfset lname_final = "#LJustify(lname_final, 15)#">
<cfset accountnumber = "#LJustify(accountnumber, 14)#">
<cfset accounttype_final = "#LJustify(accounttype_final, 3)#">
<cfset branch = "#LJustify(branch, 10)#">
<cfset amount_final = "#LJustify(amount_final, 7)#">
<cfset date_final = "#LJustify(date_final, 8)#">
<!---SET TOTAL STRING--->
<cfset total_string = "#initial##lname_final##accountnumber##accounttype_final##branch##amount_final##date_final#">
<pre>
#accountholder#<br>
#accountnumber#<br>
#accounttype#<br>
#bankname#<br>
#branch#<br>
#amount#<br>
#date#<br>
#initial#<br>
#lname_final#<br />
#accounttype_final#<br>
#amount_final#<br />
#date_final#<br>
123456789012345678901234567890123456789012345678901234567890<br />
#total_string#<br>
<br />
<br />
</pre>
<!---IF FILE FOR BANK EXISTS--->
<cfif FileExists(ExpandPath("#listgetAt('#index#',5)#.txt"))>
<!---READ EXISTING FILE HEADER--->
<cffile action="read" file="C:/ColdFusion10/cfusion/wwwroot/kelly2/#bankname#.txt" variable="bankheader">
<!---SPLIT UP THE HEADER TO ADD NEW VALUES ONTO IT--->
<cfset numericvalue = listfirst(bankheader,chr(13))>
<cfset numericvalue = #Right(numericvalue, 13)#>
<cfset RecordCountvalue = #Left(numericvalue, 3)#>
<cfset RecordCountvalue = #RecordCountvalue# + 1>
<cfset RecordCountvalue = #NumberFormat(RecordCountvalue,"000")#>
<cfset RecordCountvalue = #Left(RecordCountvalue, 3)#>
<cfset RecordCountvalue = #RJustify(RecordCountvalue, 3)#>
<cfset TotalRecordvalue = #Right(numericvalue, 10)#>
<cfset TotalRecordvalue = (#TotalRecordvalue# + #amount#) * 100000>
<cfset TotalRecordvalue = #NumberFormat(TotalRecordvalue,"0000000000")#>
<cfset TotalRecordvalue = #Left(TotalRecordvalue, 10)#>
<cfset TotalRecordvalue = #RJustify(TotalRecordvalue, 10)#>
<!---SET HEADER FOR FILE--->
<cfset fileheader_bank = "#UCase(bankname)#">
<cfset fileheader_bank = "#Left(fileheader_bank, 15)#">
<cfset fileheader_bank = "#LJustify(fileheader_bank, 16)#">
<cfset newfile_header = "#fileheader_bank##RecordCountvalue##TotalRecordvalue#">
<pre>
#numericvalue#<br />
#RecordCountvalue#<br />
#TotalRecordvalue#<br />
#newfile_header#
</pre>
<!---APPEND FILE AND ADD UPDATED HEADER--->
<cfset bankheader = listSetAt(bankheader,1,"#newfile_header#","#chr(13)#")>
<cffile action="write"
fixnewline="no"
addnewline="no"
file="#getDirectoryFromPath(getTemplatePath())#/#listgetAt('#index#',5)#.txt"
output="#bankheader#">
<!---APPEND FILE AND ADD NEW ENTRY--->
<cffile action = "append"
fixnewline="no"
file = "C:/ColdFusion10/cfusion/wwwroot/kelly2/#listgetAt('#index#',5)#.txt"
output = "#total_string#">
<!---IF FILE FOR BANK DOES NOT EXIST--->
<cfelse>
<!---SET HEADER FOR FILE--->
<cfset fileheader_bank = "#UCase(bankname)#">
<cfset fileheader_bank = "#Left(fileheader_bank, 15)#">
<cfset fileheader_bank = "#LJustify(fileheader_bank, 16)#">
<cfset newfile_header = "#fileheader_bank#001000#amount_final#">
<!---CREATE NEW FILE WITH BANK NAME--->
<cffile action = "write"
fixnewline="no"
file = "C:/ColdFusion10/cfusion/wwwroot/kelly2/#listgetAt('#index#',5)#.txt"
output = "#newfile_header#">
<!---APPEND FILE AND ADD NEW ENTRY--->
<cffile action = "append"
fixnewline="no"
file = "C:/ColdFusion10/cfusion/wwwroot/kelly2/#listgetAt('#index#',5)#.txt"
output = "#total_string#">
</cfif>
</cfloop>
</cfoutput>
I am not sure if it would be better if I start using an Array to do this, if so please advise.
I am not sure if it is possible to check as I insert each row to insert it into the correct place, I have never heard of this before.
I could also loop through the txt files at the end of the process if need be.
Read the csv file using cfhttp. The name attribute creates a query object. You can use Q of Q to do your sort and then carry on.
The details are in the documentation for cfhttp.
I'm trying to delete the file if not a spreadsheet, but keep getting the following error when I uploaded testFile.text:
ColdFusion could not delete the file D:\ColdFusion10\cfusion\runtime\work\Catalina\localhost\tmp\testFile.text
I verified its not a permission issue, because if I go back and execute the code to try to delete the file again, it will work.
<cffile action="upload" destination="#dest#" filefield="xlsfile" result="upload" nameconflict="makeunique">
<cfif upload.fileWasSaved>
<cfset theFile = dest & upload.serverFile>
<cfif isSpreadsheetFile(theFile)>
<cfspreadsheet action="read" src="#theFile#" query="data" headerrow="1">
<cfset showForm = false>
<cfelse>
<cfscript>
thisFile = theFile;
fileRead = createObject("java", "java.io.FileInputStream");
thisThread = CreateObject("java", "java.lang.Thread");
loopCT = 1;
while(1 EQ 1)
{
try
{
fileRead.init(thisFile);
break;
}
catch(any ecpt)
{
thisThread.sleep(1000);
}
incrementValue(loopCT);
if(loopCT GT 60)
{
fileRead.close();
return;
}
}
loopCT = 1;
while(1 EQ 1)
{
sizeA = fileRead.available();
thisThread.sleep(1000);
sizeB = fileRead.available();
if(sizeA EQ sizeB)
{
thisThread.sleep(1000);
sizeC = fileRead.available();
if(sizeC EQ sizeB)
{
break;
}
}
incrementValue(loopCT);
if(loopCT GT 60)
{
fileRead.close();
return;
}
}
fileRead.close();
<script type="text/javascript">
</script>
</cfscript>
<!--cffile action="delete" file="#theFile#"-->
<cfset errors = "The file was not an Excel file.">
<span style="font-size:medium;font-weight:bold; color:red"><p>The file was not an excel file!<p></span>
<input type="button" value="TRY AGAIN" class="button" onClick="window.location='bulk_upload.cfm'">
<br><br><br>
</cfif>
<cfset errors = "The file was not properly uploaded.">
</cfif>
I ran into the exact same issue with regards to cfimage locking the file and preventing deletion. The solution I came up with follows. Maybe someone can adapt to their situation:
<cflock name="[lockName]" timeout="5" >
<cfcache action="flush">
<!---[] Replace cfimage read function (CF BUG work around) --->
<cfscript>
imageObj = createObject( "java", "java.awt.Toolkit" );
imagetools = imageObj.getDefaultToolkit();
objImage = imagetools.getImage( "#fullpath#" );
<!---Whatever image I need from the image, or file...--->
<!---such as ---- myImageStruct["width"] = objImage.getWidth();--->
<!--- and then flush the object--->
objImage.flush();
</cfscript>
</cflock>
My JSON contains special characters like: new line, double quotes etc.
I am creating the JSON using Coldfusion server side script. But in case of special chars I get error due to wrongly formatted JSON. What should I do in such a case?
<cfoutput>
[
<cfset loopIndex=0>
<cfloop query="qEvents">
<cfif loopIndex NEQ 0>,</cfif>
<cfif is_allday EQ 1>
<cfset isallDayEvent = "true">
<cfelse>
<cfset isallDayEvent = "false">
</cfif>
{
"title": "#title#",
"start": "#DateFormat(start_date_time,'mm/dd/yyyy')# #TimeFormat(start_date_time,'hh:mm tt')#",
"end": "#DateFormat(end_date_time,'mm/dd/yyyy')# #TimeFormat(end_date_time,'hh:mm tt')#",
"allDay": #isallDayEvent#,
"eventID": "#event_id#",
"duration": "#duration#",
"note": "#note#",
"location": "#location#"
}
<cfset loopIndex=loopIndex+1>
</cfloop>
]
</cfoutput>
Rather than writing the JSON by hand, you should generate an array of structs and then use serializeJSON() to convert it to a valid JSON string:
<cfset thisArrayBecomesJSON = [] />
<cfloop query="qEvents">
<cfif is_allday EQ 1>
<cfset isAllDayEvent = "true" />
<cfelse>
<cfset isAllDayEvent = "false" />
</cfif>
<cfset thisEvent = {
'title' = title,
'start' = dateFormat( start_date_time, 'mm/dd/yyyy' ) & timeFormat( start_date_time, 'hh:mm tt' ),
'end' = dateFormat( end_date_time, 'mm/dd/yyyy' ) & timeFormat( end_date_time, 'hh:mm tt' ),
'allDay' = isAllDayEvent,
'eventID' = event_id,
'duration' = duration,
'note' = note,
'location' = location
} />
<cfset arrayAppend( thisArrayBecomesJSON, thisEvent ) />
</cfloop>
<cfset myJSON = serializeJSON( thisArrayBecomesJSON ) />
<cfoutput>#myJSON#</cfoutput>
This is untested, but I think it should work ok - there may be some syntax errors.