Simplifying complex if statements - if-statement

I have a matrix of values that I need to check against to show different content on a page. The vars valueA and valueB each have at least 3 possible states. The code below works fine, but I am wondering if there is a better / smarter way to handle this. Thanks!
<cfif valueA EQ -1 AND valueB EQ -1>
<!--- Do something --->
<cfelseif valueA EQ -1 AND valueB EQ 0>
<!--- Do something --->
<cfelseif valueA EQ -1 AND valueB EQ 1>
<!--- Do something --->
<cfelseif valueA EQ -1 AND valueB GT 1>
<!--- Do something --->
<cfelseif valueA EQ 0 AND valueB EQ -1>
<!--- Do something --->
<cfelseif valueA EQ 0 AND valueB EQ 0>
<!--- Do something --->
<cfelseif valueA EQ 0 AND valueB EQ 1>
<!--- Do something --->
<cfelseif valueA EQ 0 AND valueB GT 1>
<!--- Do something --->
<cfelseif valueA EQ 1 AND valueB EQ -1>
<!--- Do something --->
<cfelseif valueA EQ 1 AND valueB EQ 0>
<!--- Do something --->
<cfelseif valueA EQ 1 AND valueB EQ 1>
<!--- Do something --->
<cfelseif valueA EQ 1 AND valueB GT 1>
<!--- Do something --->
<cfelseif valueA GT 1 AND valueB EQ -1>
<!--- Do something --->
<cfelseif valueA GT 1 AND valueB EQ 0>
<!--- Do something --->
<cfelseif valueA GT 1 AND valueB EQ 1>
<!--- Do something --->
<cfelseif valueA GT 1 AND valueB GT 1>
<!--- Do something --->
</cfif>

The ColdFusion engine won't see complexity in the code above. The humans developers will have a bit of hard time (although I'm sure there are some who will have no problem to parse that, (not me)).
Here is my version, which with the use of editor's code folding, it makes easier to read, at least for me.
<cfif valueA EQ -1>
<cfif valueB EQ -1>
<!--- Do something --->
<cfelseif valueB EQ 0>
<!--- Do something --->
<cfelseif valueB EQ 1>
<!--- Do something --->
<cfelseif valueB GT 1>
<!--- Do something --->
</cfif>
<cfelseif valueA EQ 0>
<cfif valueB EQ -1>
<!--- Do something --->
<cfelseif valueB EQ 0>
<!--- Do something --->
<cfelseif valueB EQ 1>
<!--- Do something --->
<cfelseif valueB GT 1>
<!--- Do something --->
</cfif>
<cfelseif valueA EQ 1>
<cfif valueB EQ -1>
<!--- Do something --->
<cfelseif valueB EQ 0>
<!--- Do something --->
<cfelseif valueB EQ 1>
<!--- Do something --->
<cfelseif valueB GT 1>
<!--- Do something --->
</cfif>
<cfelseif valueA GT 1>
<cfif valueB EQ -1>
<!--- Do something --->
<cfelseif valueB EQ 0>
<!--- Do something --->
<cfelseif valueB EQ 1>
<!--- Do something --->
<cfelseif valueB GT 1>
<!--- Do something --->
</cfif>
</cfif>
I hope it helps.

To show you how the different options behave differently, I created a fiddle with the 2 cfif options so-far presented and added a switch option with a little bit of math magic to make a switch work.
The whole fiddle is at: https://trycf.com/gist/041d1a6c3b3701d30306a9c80f1ddbea/acf2018?theme=monokai
My switch code:
local.valA = arguments.valueA>1?99:arguments.valueA ; // If GT 1, then 99
local.valB = arguments.valueB>1?99:arguments.valueB ; // If GT 1, then 99
local.finalOut = "" ;
switch (valA*valB) { // multiply the two vals and it will be -1,0,1,99,-99 or -99*99
case -1 : // One is -1 and One is 1 >> 1*-1=-1
if (valA == 1) {
finalOut = "1,-1" ;
} else {
finalOut = "-1,1" ;
}
break ;
case 1 : // Both are either 1 or -1 >> -1*-1=1 | 1*1=1
if (valA == 1) {
finalOut = "1,1" ;
} else {
finalOut = "-1,-1" ;
}
break ;
case 0 : // One is 0 >> 0*x=0
if (valA == 0) {
switch (valB) {
case -1 :
finalOut = "0,-1" ;
break;
case 0 :
finalOut = "0,0" ;
break;
case 1 :
finalOut = "0,1" ;
break;
case 99 :
finalOut = "0,99" ;
break;
}
} else {
switch (valA) {
case -1 :
finalOut = "-1,0" ;
break;
case 1 :
finalOut = "1,0" ;
break;
case 99 :
finalOut = "99,0" ;
break;
}
}
break ;
default : // One > 1, Other not 0
switch (ValA) {
case -1 : // ValA is -1, ValB is >1
finalOut = "-1,99" ;
break ;
case 1 : // ValA is 1, ValB is >1
finalOut = "1,99" ;
break ;
case 99 : // ValA is >1, ValB <> 0
switch (valB) {
case 1 :
finalOut = "99,1" ;
break ;
case -1 :
finalOut = "99,-1" ;
break ;
case 99 :
finalOut = "99,99" ;
break;
}
break ;
}
break ;
}
My Fiddle puts each of the 3 options in a function, so I could loop through them multiple times to demonstrate them and check timing. See the Fiddle above for those functions.
A couple of notes: as far as performance, you definitely want to try to short-circuit the logic as much as possible. Both my switch and Alex's cfif solutions were a bit faster than the original, un-nested cfif. That demonstrates the value of nesting the options to bypass some logic.
A cfif works from top to bottom. When I changed the array to add about 100K rows and mostly include values that would meet conditions early in the cycle without having to go through many options, all three options performed very similarly. If I included conditions that would fall at the end of the options, then the original cfif performed about a third slower than the other two options that short-circuited logic, with the cfif and the switch performing very similarly.
I tested with around 1M, 100K and 10K rows. With fewer rows, the performance was very close on all 3. I assume that's likely because of the variable instantiation and math checks being fairly consistent (despite number of iterations) and not being needed in the cfif versions.
One thing that I didn't test was tags vs script. I wrote my switch in script-syntax, and it will probably be slightly faster in tag-syntax. I also didn't test either of the cfifs in script. That might make a slight difference. Moving around the logic will also possibly change speed of the options.
TL;DR: Since I believe you said you will be using this to only evaluate one set of values, then Alex's solution will probably work best. I think you should go with what's easiest to read and interpret here. My example was mostly because you asked about performance. :-)

Related

How can i get the last match of the loop over the structure

i am running a loop :
<cfset rulecounter = 1>
<cfloop collection="#mystucts#" index="key">
<cfif rulecounter neq thevaluewhere it should match>
OR
</cfif>
<cfset rulecounter = rulecounter + 1>
</cfloop>
so trying to do a match here if it is the last item in struct, do not add OR, else keep adding OR

How to output the values of a query depending on the value of row?

If I have a query result like this
type_name | count
-----------------
test1 | 5
test2 | 4
test4 | 7
How to output the count base on the value of the column type_name?
From the table the row value 'test3' doesn't exist, but it doesn't mean it wont exist later after a refresh.
With the below code I will only get the value but looping 3 times since test3 value doesn't exist.
<cfoutput name="query">
<table>
<tr><td><cfif query.type_name eq 'test1'>#query.count#</cfif></td></tr>
<tr><td><cfif query.type_name eq 'test2'>#query.count#</cfif></td></tr>
<tr><td><cfif query.type_name eq 'test3'>#query.count#</cfif></td></tr>
<tr><td><cfif query.type_name eq 'test4'>#query.count#</cfif></td></tr>
</cfoutput>
<cfset testResults = [0,0,0,0]>
<cfloop query="query">
<cfset testResults[right(query.type_name, 1)] = query.count>
</cfloop>
<table>
<cfloop array="#testResults#" index="count">
<td>#count#</td>
</cfloop>
</table>
You should see
5
4
0
7
If you need a more dynamic approach, you can transform your full query result (all columns) to an array of structs:
<!---
<!--- test data --->
<cfset query = queryNew("type_name,count", "VARCHAR,INTEGER")>
<cfset queryAddRow(query, 3)>
<cfset querySetCell(query, "type_name", "test1", 1)>
<cfset querySetCell(query, "count", "5", 1)>
<cfset querySetCell(query, "type_name", "test2", 2)>
<cfset querySetCell(query, "count", "4", 2)>
<cfset querySetCell(query, "type_name", "test4", 3)>
<cfset querySetCell(query, "count", "7", 3)>
--->
<!--- number of tests to list --->
<cfset expectedNumberOfRows = 4>
<!--- remember all columns while transforming query to array of structs --->
<cfset queryColumns = listToArray(query.columnList)>
<!--- initialize array of structs --->
<cfset testResults = arrayNew(1)>
<cfloop from="1" to="#expectedNumberOfRows#" index="i">
<cfset blankResult = structNew()>
<!--- initialize all columns for each row --->
<cfloop array="#queryColumns#" index="columnName">
<cfset blankResult[columnName] = "">
</cfloop>
<!--- default values for specific columns --->
<cfset blankResult["type_name"] = "test#i#">
<cfset blankResult["count"] = "0">
<cfset testResults.add(blankResult)>
</cfloop>
<!--- transfer cell values from each query row to array of structs --->
<cfset queryRowIndex = 1>
<cfloop query="query">
<!--- extract possible row index --->
<cfset testNumber = trim( reReplace(query.type_name, "[^0-9]*([0-9]+)$", "\1") )>
<!--- transfer cells --->
<cfif reFind("^[0-9]+$", testNumber)>
<cfloop array="#queryColumns#" index="columnName">
<cfset testResults[int(testNumber)][columnName] = query[columnName][queryRowIndex]>
</cfloop>
</cfif>
<cfset queryRowIndex++>
</cfloop>
<cfoutput>
<table>
<cfloop array="#testResults#" index="testResult">
<tr>
<td>#testResult.type_name#</td>
<td>#testResult.count#</td>
<!--- add additional columns if desired --->
</tr>
</cfloop>
</table>
</cfoutput>

shorten numbering e.g., 1,2,5,6,7 becomes 1,2,5-7

I have been tasked with shortening a sequence of numbers by replacing internal sequences of 3 or more with a dash for the internal numbers (as in, 3,5,6,7,8,10 would be 3,5-8,10)
Pretty similar to the above, just using lists.
<cfscript>
a = listToArray('3,5,6,7,8,10,11,15,16,17,18');
writeOutput( fNumSeries(a) );
string function fNumSeries( array a) output="false" {
var tempList = '';
var outputList = '';
for ( var i in a ) {
if( listLen(tempList) == 0 ) {
// Nothing in the list
tempList = i;
} else if ( listLen(tempList) ) {
if ( listLast(tempList)+1 == i) {
// i is next in order from the previous list item. Just add to temp list.
tempList = listAppend(tempList, i);
} else if (listLen(tempList) >= 3) {
// current item is not the next item, and the tempList is more than 2 items long, so abbreviate and set templist to the current item.
outputList = listAppend(outputList, listFirst(tempList) & '-' & listLast(tempList));
tempList = i;
} else {
// tempList is less than 3 items long, just append to output and set tempList to the current item.
outputList = listAppend(outputList, tempList);
tempList = i;
}
}
}
// Clean up the remaining item(s) in tempList. If 3 or more, abbreviate, else just append to output.
outputList = listLen(tempList) >= 3 ? listAppend(outputList, listFirst(tempList) & '-' & listLast(tempList)) : listAppend(outputList, templist);
return outputList;
}
</cfscript>
I put the following function together, plus some demo output. It assumes the numbers are in an ordered, non-repeating array. (CF list functions help get to this point.) Is there a more elegant way of doing this?
<cffunction name="fNumSeries" output="1" hint="parses array of numbers for display: 1|2|3|6|7|9|10|11|12 would be 1-3, 6, 7, 9-12 ">
<cfargument name="raNums" required="1" type="array">
<cfset raLen=arraylen(raNums)>
<cfif raLen eq 0><CFRETURN "">
<cfelseif raLen eq 1><CFRETURN raNums[1]>
<cfelseif raLen eq 2><CFRETURN raNums[1] & "," & raNums[2]>
</cfif>
<cfset numSeries="">
<cfset numHold=raNums[1]>
<cfset seqHold=raNums[1]>
<cfset numSeries=raNums[1]><!--- raNums[1]always output the first number --->
<cfloop from="1" to="#raLen#" index="idxItem">
<cfif idxItem gt 1><!--- wait for 2nd element to decide output --->
<cfif numHold+1 neq raNums[idxItem]><!--- reason to output and seqHold+1 neq numHold--->
<cfif seqHold eq numHold>
<cfset numSeries=numSeries & ",#raNums[idxItem]#">
<cfelse>
<cfif seqHold+1 eq numHold><cfset numSeries=numSeries & ","><!--- and numHold+1 neq raNums[1] and seqHold+1 eq numHold --->
<cfelse><cfset numSeries=numSeries & "-">
</cfif>
<cfset numSeries=numSeries & "#numHold#,#raNums[idxItem]#">
</cfif>
<cfset seqHold=raNums[idxItem]>
</cfif>
</cfif>
<cfset numHold=raNums[idxItem]>
</cfloop>
<cfif seqHold neq numHold><!--- display the last element --->
<cfif seqHold+1 eq numHold or raLen eq 2><cfset numSeries=numSeries & ",">
<cfelse><cfset numSeries=numSeries & "-">
</cfif>
<cfset numSeries=numSeries & "#numHold#">
</cfif>
<CFRETURN numSeries>
</cffunction>
<cfset raRa=[
[2,12],
[2,3,12],
[2,4,12],
[2,3,4,12],
[2,8,9,11,12],
[2,9,11,12],
[2,3,8,10,11,12]
]>
<cfoutput>
<cfloop array="#raRa#" index="ra">
#arrayToList(ra)#<br />
#fNumSeries(ra)#<hr>
</cfloop>
</cfoutput>

How to Add a Calculated Value to Export to Excel Function

I have a block of code that sets an "Aging" value for an invoice, as part of an export to excel script. I have all the other options in the export function working, except the "Aging" value.
Here is the SQL code for how "Aging" works:
<cfif Aging neq "">
<cfswitch expression="#Aging#">
<cfcase value=29>
AND I.DueDate BETWEEN dateadd(day, datediff(day, 0 ,getdate())-29, 0) AND dateadd(day, datediff(day, 0 ,getdate()), 0)
AND I.OutstandingAmount > 0
</cfcase>
<cfcase value=59>
AND I.DueDate BETWEEN dateadd(day, datediff(day, 0 ,getdate())-59, 0) AND dateadd(day, datediff(day, 0 ,getdate())-30, 0)
AND I.OutstandingAmount > 0
</cfcase>
<cfcase value=89>
AND I.DueDate BETWEEN dateadd(day, datediff(day, 0 ,getdate())-89, 0) AND dateadd(day, datediff(day, 0 ,getdate())-60, 0)
AND I.OutstandingAmount > 0
</cfcase>
<cfcase value=90>
AND I.DueDate < dateadd(day, datediff(day, 0 ,getdate())-90, 0)
AND I.OutstandingAmount > 0
</cfcase>
</cfswitch>
</cfif>
I'm new to ColdFusion and I just cannot figure out how to write the code for the Excel part. Here is the export function, with line breaks added for readability only.
<!--- ------------------------------------------ --->
<cfelseif ExType eq "xls5">
<!--- ------------------------------------------ --->
<cfset tabchar = Chr(9)>
<cfset NewLine = Chr(13) & Chr(10)>
<cfcontent type="text/msexcel">
<cfheader name="Content-Disposition" value="Attachment; filename=Late/Aging.xls">
<cfoutput>DATE INVOICED#TabChar#INVOICE#TabChar#CLIENT#TabChar#TOTAL#TabChar#AMOUNT DUE#TabChar#AGE#NewLine#</cfoutput>
<cfoutput query="viewinvoices">
<cfif curr.currencytype eq "Euro">
<cfset TotalC = #LSEuroCurrencyFormat(TotalCost, "local")#>
<cfelse>
<CFSET oldlocale = SetLocale(#curr.currencytype#)>
<cfset TotalC = #LSCurrencyFormat(TotalCost, "local")#>
</cfif>
<cfif curr.currencytype eq "Euro">
<cfset OutAmt = #LSEuroCurrencyFormat(OutstandingAmount, "local")#>
<cfelse>
<CFSET oldlocale = SetLocale(#curr.currencytype#)>
<cfset OutAmt = #LSCurrencyFormat(OutstandingAmount, "local")#>
</cfif>
#DateFormat(InvoiceDate, 'mm/dd/yyyy')##TabChar#
<cfif curr.CustomInvoices eq "0">
#Invoice#
<cfelse>
#NumberFormat(InvoiceCustom, '0000')#
</cfif>
#TabChar##Client##TabChar##TotalC##TabChar##OutAmt##TabChar##Aging##Newline#
</cfoutput>
I have this in my export statement but does not seem to work:
<cfif Aging eq "29">
<cfset Aging = 'Year(s)'>
<cfelseif Aging eq "59">
<cfset Aging = 'Day(s)'>
<cfelseif Aging eq "89">
<cfset Aging = 'Month(s)'>
<cfelseif Aging eq "90">
<cfset Aging = 'Quarter(s)'>
<cfelse>
<cfset Aging = 'n/a'>
</cfif>
The text years, etc is just for testing.

ColdFusion: Get variable string value from cfloop

The code below outputs weekend dates in the current month.
CODE:
<cfparam name="month" default="#DatePart('m', Now())#">
<cfparam name="year" default="#DatePart('yyyy', Now())#">
<cfset ThisMonthYear=CreateDate(year, month, '1')>
<cfset Days=DaysInMonth(ThisMonthYear)>
<cfset ThisDay = 1>
<cfloop condition="ThisDay LTE Days">
<cfset presentDay = CreateDate(year, month, thisday)>
<cfif DayOfWeek(presentDay) EQ '7'>
<cfoutput>#ThisDay#</cfoutput>
<cfelseif DayOfWeek(presentDay) EQ '1'>
<cfoutput>#ThisDay#</cfoutput>
</cfif>
<cfset ThisDay = ThisDay + 1>
</cfloop>
OUTPUT:
6 7 13 14 20 21 27 28
What I'm trying is to pass value of this cfloop in one variable. The code below only displays the last weekend date value.
CODE:
<cfset ThisDay = 1>
<cfset weekDayOfMonth = "">
<cfloop condition="ThisDay LTE Days">
<cfset presentDay = CreateDate(year, month, thisday)>
<cfif DayOfWeek(presentDay) EQ '7'>
<cfset weekDayOfMonth = ThisDay>
<cfelseif DayOfWeek(presentDay) EQ '1'>
<cfset weekDayOfMonth = ThisDay>
</cfif>
<cfset ThisDay = ThisDay + 1>
</cfloop>
<cfoutput>#weekDayOfMonth#</cfoutput>
OUTPUT
28
Question, what do I need fix in my last cfloop code so I can pass loop values into the jsWeekendDates variable?
Any help will be greatly appreciated.
Thank you.
Just figured on my own. Enjoy.
<cfset ThisDay = 1>
<cfset weekDay = "">
<cfloop condition='ThisDay LTE Days'>
<cfset presentDay = CreateDate(year, month, thisday)>
<cfif DayOfWeek(presentDay) EQ '1' OR DayOfWeek(presentDay) EQ '7'>
<cfset weekDay = weekDay & " " & ThisDay">
</cfif>
<cfset ThisDay = ThisDay + 1>
</cfloop>
<cfoutput>#weekDay#</cfoutput>