Split the session variable into an array using recreating individual session variables - coldfusion

The typical way government offices make you record mileage is to enter one number per input box like the example below:
So what I am trying to do is create one input box that can split the session variable into an array. Then when that session variable is split into an array I would like it to set each value in the array to its own session variable.
Mileage Input:
123456 -<cfoutput>#session.checkout.vehicle.mileage#</cfoutput>
array [1,2,3,4,5,6]
6 -<cfoutput>#session.checkout.vehicle.mileage1#</cfoutput>
5 -<cfoutput>#session.checkout.vehicle.mileage2#</cfoutput>
4 -<cfoutput>#session.checkout.vehicle.mileage3#</cfoutput>
3 -<cfoutput>#session.checkout.vehicle.mileage4#</cfoutput>
2 -<cfoutput>#session.checkout.vehicle.mileage5#</cfoutput>
1 -<cfoutput>#session.checkout.vehicle.mileage6#</cfoutput>
So then I will be able to prefill in an already created form that has the boxes split for only one per box.
Where I am super confused and trying to comprehend is that there will not always be 6 variables. Let's say the mileage is 2344. I am assuming it will need to know to start backwards, counting from the right to the left. That's why I started 6 at #session.checkout.vehicle.mileage1#
Hopefully I have not super confused anyone with what I am trying to do. And any help would be greatly appreciated!!
<cfparam name="form.mileage" default="#session.checkout.vehicle.mileage#">
...
<label for="mileage">Mileage:</label>
<input type="text" name="mileage"
id="mileage"
value="<cfoutput>#form.mileage#</cfoutput>">
Edit:
The issue I am having with this is let's say the mileage is 9000 all 0's will not show. (which is great for the first two zero's in (009000) but after the 9 those 0's would still need to show.) Do you any ideas for that issue? Or should this be a new question?
<cfset Mileage = "9000" />
<cfif mileage is not "Exempt">
<cfset Mileage = NumberFormat(trim(Mileage),"000000") />
<cfset MilArray = ReMatch("\d",Mileage) />
<cfelse>
<cfset MilArray = ["E","x","e","m","p","t"]>
</cfif>
<cfdump var="#MilArray#">
<cfif MilArray[1] is not "0">
<!---Section6 First box Odometer Reading--->
<cfpdfformparam name="E" value="#MilArray[1]#">
<cfelse>
<cfpdfformparam name="E" value="">
</cfif>

If I'm understanding, you want to divide the string into six easy to work with variables, or whatever the length of the variable is.
<cfset Mileage = "123456" />
<cfset MilArray = ReMatch("\d",Mileage) />
<cfdump var="#MilArray#" />
You can actually stick a Reverse() in there to reverse the string, this may be handy because you can have [1] at the ones place, [2] at tens, [3] at hundreds.
<cfset Mileage = "123456" />
<cfset MileageR = Reverse(Mileage) />
<cfset MilArray = ReMatch("\d",MileageR) />
<cfdump var="#MilArray#" />
\d by itself in regular expressions just means "one digit". It's the same as [0-9].
As the CFDUMP will show, ReMatch will split your mileage into an easy to work with array. If you use the reverse as above, you can say "The last digit of your mileage is #MilArray[1]#.", as an example.
Edit:
you know the \d ? is there a way to have it be either \d or only the word Exempt? is it possible to create both of those?
There are a few ways.
You can say
<cfif mileage is not "Exempt">
...
<cfelse>
<cfset MilArray = ["Exempt"]>
</cfif>
which creates a one dimensional array populated with "Exempt" as the only element, which might be useful later in your code so you know MilArray is always an array, or you can simply always work with the <cfif mileage is not "Exempt">.
A regex to accomplish the same thing is possible but it achieves the same as the above cfif, and you'd have to write exempt backwards if you're using reverse, like this
<cfset MilArray = ReMatchNoCase("\d|^Exempt$|^tpmexE$)",trim(Mileage)) />
<cfif MilArray[1] is "tpmexE"><cfset milArray = ["Exempt"] /></cfif>
Edit #2:
<cfif isDefined("session") and structKeyExists(session, 'checkout') and structKeyExists(session.checkout, 'info') and structKeyExists(session.checkout.info, 'oreading')>
<cfif isDefined("#MilArray[6]#") eq "">
<cfpdfformparam name="E" value="">
<!---Section6 First box Odometer Reading--->
<cfelse>
<cfpdfformparam name="E" value="#MilArray[6]#">
</cfif>
</cfif>
This is a task for ArrayIsDefined() (link)
<cfif isDefined("session") and structKeyExists(session, 'checkout') and structKeyExists(session.checkout, 'info') and structKeyExists(session.checkout.info, 'oreading')>
<cfset MilArray = ReMatch("\d",session.checkout.info.oreading) />
<cfif not ArrayIsDefined(MilArray,6)>
<cfpdfformparam name="E" value="">
<!---Section6 First box Odometer Reading--->
<cfelse>
<cfpdfformparam name="E" value="#MilArray[6]#">
</cfif>
.... I assume that it continues on down from here... <cfif not ArrayIsDefined(MilArray,5)>........</cfif>
</cfif>
Finally, while there's contention here on whether to use StructKeyExists() over IsDefined(), there's a narrow field where isDefined() fails.
(Don't put structures in the top level and in the variables scope. Cold Fusion gets confused--IE, don't create an object called "variables.form" or "variables.url"). Beyond that, It's mostly just semantics.
Anyway. once you have the above code working (because it's your code and your familiar with it), you might find it useful to switch to the easier to read IsDefined() version, because isDefined can check several levels deep in one condition.
<cfif isDefined("session.checkout.info.oreading')>
<cfset MilArray = ReMatch("\d",session.checkout.info.oreading) />
<cfif not ArrayIsDefined(MilArray,6)>
<cfpdfformparam name="E" value="">
<!---Section6 First box Odometer Reading--->
<cfelse>
<cfpdfformparam name="E" value="#MilArray[6]#">
</cfif>
</cfif>
Edit 3:
Leigh points out
Why so complicated? Can't you just left pad the value with spaces or zeroes? Then change the regex to check for either a digit or space? Then the array will always have six elements
This can be achieved like this:
<cfset Mileage = "exempt" />
<cfif mileage is not "Exempt">
<cfset Mileage = NumberFormat(trim(Mileage),"000000") />
<cfset MilArray = ReMatch("\d",Mileage) />
<cfelse>
<cfset MilArray = ["E","x","e","m","p","t"]>
</cfif>
<cfdump var="#MilArray#">
Which would conveniently drop Exempt into place (handy that it's 6 characters).
You need to do some prechecking before you start generating the pdf to make sure that mileage variable is Exempt or or numeric.
<cfif len((trim(mileage)) gt 6 or not ((isNumeric(trim(mileage))
or mileage is "exempt")>
<!--- The 6 above is a len-check, you may need to update that number to
something else later, but you'll have to put the same number of 0s
in the NumberFormat function.
If you change that number, and the 0s, you'll need to pad the
"Exempt array"... ie ["E","x","e","m","p","t"," "] --->
....raise a flag...
</cfif>

Here is a simpler way.
originalNumber = "123";
sixDigitNumber = right(("000000" & originalNumber), 6);
<cfoutput>
<cfloop from="1" to = "6" index="position">
do something useful with #Mid(sixDigitNumber, position, 1)#
</cfloop>
<cfoutput>

Related

CFIF For array variable not always existing

I am trying to take a session variable and break it up into an array in order to create separate session variables.
What I am trying to do is have one textbox to find the feet and inches of a length. For Example 15.2 would be 15 feet and 2 inches
<cfif isDefined("session")
and structKeyExists(session, 'checkout')
and structKeyExists(session.checkout, 'vehicle')
and structKeyExists(session.checkout.vehicle, 'ft') >
<cfset Feet = "#session.checkout.vehicle.ft#" />
<cfset FeetArray = listtoarray(Feet, ".") />
<cfdump var="#FeetArray#">
</cfif>
I could then use #FeetArray[1]# & #FeetArray[2]# for feet and inches.
Where I am struggling is sometimes inches will not be entered so FeetArray[2] will not be needed sometimes.
I tried using:
<cfif isDefined("#FeetArray[2]#")>
<cfif isDefined("FeetArray[2]")>
neither of them worked I was just wondering if someone could please guide me in the correct direction!
To check if an element in an array exists, use (the poorly-named) arrayIsDefined() function:
<cfif arrayIsDefined(FeetArray, 2)>
Please note: you really oughtn't be using tag-based syntax for business logic code like this. Preserve your tags for your views, where they belong.
You can check the length of array and according to that you can do your coding.
<cfif isDefined("session") and structKeyExists(session, 'checkout') and structKeyExists(session.checkout, 'vehicle') and structKeyExists(session.checkout.vehicle, 'ft') >
<cfset Feet = "#session.checkout.vehicle.ft#" />
<cfset FeetArray = listToArray(Feet, ".") />
<cfif arrayLen(FeetArray) EQ 1>
code when there is feet available
<cfelse>
code when there are both feet and inches available
</cfif>
</cfif>

Checking for key existence in structure

I have a variable named #cfData# which contains an array of structures. As it's clear from the image, for 1st structure array there are 6 keys and for 2nd, only two keys,viz date and open.
If I run a common loop, to go through each and every key, I will get an error at second array element. So the following only works when all the keys are present in the structure:
<cfset blockedtotal = 0 />
<cfset bouncetotal = 0 />
<cfset blocked = 0/>
<cfset datetotal = 0 />
<cfloop array = #cfData# index = "i">
<cfset blockedtotal += i.blocked />
<cfset bouncetotal += i.bounce />
</cfloop>
After reading online, I got an idea of using StructKeyExists where I think I can proceed in the following way:
<cfif structKeyExists(cfData,"bounce")>
<cfoutput>Bounce:#cfData.bounce#"/></cfoutput>
<cfelse>
<cfoutput> Bounce : [none]<br/></cfoutput>
</cfif>
But I am wondering, where exactly should I insert the above code inside the cfloop? Please advise if my approach is wrong.
Update:
Thanks guys. I got it running by using the following code based on the answers and it's running fine:
<cfloop array="#cfData#" index="i">
<cfif structKeyExists(i, "date")>
<cfset counter++>
<cfoutput>#counter#</cfoutput> Date is: <cfoutput> #i.date#</cfoutput> <br/>
</cfif>
</cfloop>
you don't need a "common loop". You can loop through each struct with
<cfloop array="#cfData#" index="i">
<cfloop collection="#i#" item="key">
struct with key '#key#' has data: #i[key]#
</cfloop>
</cfloop>
Of, if you need to decide if the struct has certain key, do something:
<cfloop array="#cfData#" index="i">
<cfif structKeyExists(i, "someKey")>
<cfset counter++>
</cfif>
</cfloop>

ColdFusion isDefined

I am trying to check to see if data exist in my form If data does not exist I want to assign it to O. How can I do this.
<cfif not isDefined("FORM.Age")>
cfset FORM.Age = "0"
<cfif>
Generally the best practice is considered to be to avoid isDefined. This is because isDefined will search all scopes until it finds a matching variable. So it's more efficient to use structKeyExists, eg:
<cfif NOT structKeyExists(form, "age")>
<cfset form.age = 0>
</cfif>
Also, another way to achieve this is to use cfparam, and specify 0 as the default:
<cfparam name="form.age" default="0">
You're almost there:
<cfif not isDefined("FORM.Age")>
<cfset Form.Age = 0>
</cfif>
Technically what you have is fine once you enclose the cfset in tags < and >. Assuming that omission is just a typo, could it be you are trying to use it with a text field?
Text fields always exist on submission. The value may be an empty string, but the field itself still exists, so IsDefined will always return true. If that is the case, you need to examine the field length or value instead. Then do something if it is empty according to your criteria. For example:
<!--- value is an empty string --->
<cfif NOT len(FORM.age)>
do something
</cfif>
... OR
<!--- value is an empty string or white space only --->
<cfif NOT len(trim(FORM.age))>
do something
</cfif>
... OR
<!--- convert non-numeric values to zero (0) --->
<cfset FORM.Age = val(FORM.Age)>
There are actually two things you want to to ensure. First, make sure this page was arrived at by submitting the proper form. Next, ensure you have a numeric value for the form.age variable. Here is an example of how you might want to code this:
<cfif StructKeyExists(form, "age") and cgi.http_referrer is what it should be>
<cfif IsNumeric(form.age) and form.age gt 0>
<cfset AgeSubmitted = int(form.age)>
<cfelse>
<cfset AgeSubmitted = 0>
</cfif>
...more code to process form
<cfelse>
...code for when page was not arrived at properly
</cfif>

Coldfusion: Simple cfgridupdate validation

I trying to use a very simple CFGRID (with CFGRIDUPDATE) but not allow NULLS.
<cfset strWarning= "">
<cfif IsDefined("FORM.gridEntered")>
<cfif FORM.myName EQ '' OR FORM.myURL EQ ''>
<cfset strWarning= "Error: Form fields cannot be blank">
<cfelse>
<cfgridupdate grid="gridSomething"
dataSource="qryTSQL" tableName="tblName" keyOnly="Yes">
</cfif>
</cfif>
<cfoutput>#strWarning#</cfoutput><br />
<cfform>
<cfgrid name="gridSomething" query="qryTSQL"
selectMode="Edit" format="HTML">
<cfgridcolumn name = "myID" display="No">
<cfgridcolumn name = "myName">
<cfgridcolumn name = "myURL">
</cfgrid>
<cfinput type="submit" name="gridEntered">
The error I am getting is - Error Diagnostics: Complex object types cannot be converted to simple values. The expression has requested a variable or an intermediate expression result as a simple value. However, the result cannot be converted to a simple value. Simple values are strings, numbers, boolean values, and date/time values. Queries, arrays, and COM objects are examples of complex values.
The most likely cause of the error is that you tried to use a complex value as a simple one. For example, you tried to use a query variable in a cfif tag.
I think I am under thinking this desired solution.
Thanks.
EDIT: Updated Error Message
Error Diagnostics: Element MYURL is undefined in FROM.
Form scope - struct
FIELDNAMES GRIDENTERED,__CFGRID__CFFORM_1__GRIDSOMETHING
GRIDENTERED Submit Query
GRIDSOMETHING.MYID Form scope - array
1 1001
GRIDSOMETHING.MYURL Form scope - array
1 /test_d.cfm
GRIDSOMETHING.ORIGINAL.MYID Form scope - array
1 1001
GRIDSOMETHING.ORIGINAL.MYURL Form scope - array
1 /changed.cfm
GRIDSOMETHING.ROWSTATUS.ACTION Form scope - array
1 U
__CFGRID__CFFORM_1__GRIDSOMETHING __CFGRID__EDIT__=2 MYID Y MYURL Y 1 U 1001 1001 /test_d.cfm /changed.cfm
Grids work differently than standard FORM fields. Since grids contain multiples rows of data, CF creates arrays of the new/changed values. One for each grid column ie
FORM.gridname.columnName
To determine if any of the values for a specific column are empty, you need to loop through the array for that column and check each value:
<cfset updateCount = arrayLen(FORM.gridSomething.myName) />
<cfloop from="1" to="#updateCount#" index="x">
<cfset nameValue = trim( FORM.gridSomething.myName[x] ) />
<cfset urlValue = trim( FORM.gridSomething.myURL[x]) ) />
<!--- one or both of the values is empty --->
<cfif not len( nameValue ) OR not len( urlValue )>
... empty value found. do something here ....
</cfif>
</cfloop>
Add this function to you code and call it passing it the FORM scope as follows;
On page or CFC works the same!
Clears up errors with some versions of CF that choke on blank cols in a simple grid update
<cffunction name="cleanGridUpdate" >
<cfargument name="FORM" >
<cfloop collection="#FORM#" item="thisOne">
<cftry>
<cfset thisDeep = arrayLen(FORM[thisOne])>
<cfloop index="x" from="1" to="#thisDeep#">
<cfif len(FORM[thisOne][x])lt 1>
<cfset FORM[thisOne][x] = javaCast( "null", 0 )>
</cfif>
</cfloop>
<cfcatch>
<cfset cat=1>
</cfcatch>
</cftry>
</cfloop>
<cfreturn form>
</cffunction>
<cfif isDefined('URL.action') AND URL.action eq 'gridUpdate'>
<cfset cleanGridUpdate(FORM)>
<cfgridupdate grid="#URL.table#" table="#URL.table#" datasource="your_DS"
keyonly="yes" >
</cfif>
<cfform action="##?action=gridUpdate&table=cheesePuffs" method="post">
<cfgrid name='cheesePuffs' format='HTML'/>
<cfinput name="submit" type="submit" value>
</cfform>

How To Loop Through Two Lists?

I need to join the output of two separate lists together to output in a CFMAIL, and I'm wondering what the best way to approach this is.
I have two form fields: first_name and last_name
The fields have up to 5 names in each. I need to loop through those names and join the first and last names, then output them to unordered list. I am having trouble visualizing what the right approach to accomplish this is.
Can someone suggest a method in CFML (I don't know CFSCRIPT very well).
Thanks!
EDIT: I should have added that both fields will always have the exact same number of entries. Thanks to all that answered -- proof that there are a lot of ways to skin a cat :)
I would do something like
<cfloop from="1" to="#ListLen(firstnames)#" index="i">
#ListGetAt(firstnames,i)# #ListGetAt(lastnames,i)#<br>
</cfloop>
If this were a list of 5000 you would be better off putting it in a structure or an array, but for a list of ~5 this should be sufficient.
I think this would be the easiest way to accomplish this.
<!--- Create a names container --->
<cfset names = "<ul>">
<!--- Fill some dummy containers --->
<cfset first = "thomas,henry,philip,john,rony">
<cfset last = "smith,baker,crowe,ryan,jones">
<!--- Loop through the lists and append them to the container string --->
<cfloop index="name" to="#listLen(first)#" from="1">
<cfset names &= "<li>" & ListGetAt(first,name) & " " & ListGetAt(last,name) & "</li>">
</cfloop>
<cfset names &= "</ul>">
<cfoutput>#names#</cfoutput>
I would add in a check to make sure that your list values exists at each index, otherwise you will get errors. I would also add in a check to loop through whichever list is greater so that you get all values just in case someone doesn't enter exactly 5 in both:
<Cfset firstnames="Matt,Ian,Brandon,Sam,Tom">
<cfset lastnames="Jones,Smith,Weiss">
<!--- SEE WHICH LIST IS LONGER AND SET THAT AS THE ONE THAT WE WILL USE FOR THE LOOP --->
<cfif ListLen(firstnames) gte ListLen(lastnames)>
<cfset primary=firstnames>
<cfelse>
<cfset primary=lastnames>
</cfif>
<cfset myOutput="<ul>">
<cfloop from="1" to="#ListLen(primary)#" index="i">
<Cfset myOutput &= "<li>">
<cfif ListLen(firstnames) gte i>
<cfset myOutput &= ListGetAt(firstnames,i)>
</cfif>
<cfif ListLen(lastnames) gte i>
<cfset myOutput &= " " & ListGetAt(lastnames,i)>
</cfif>
<Cfset myOutput &= "</li>">
</cfloop>
<Cfset myOutput &= "</ul>">
<cfoutput>#myOutput#</cfoutput>
You could use the "list" attribute with CFLOOP although it means combining list functions within the output. Here is an example though of how it could be done and it makes the assumption the two lists will always have the same lengths. If these names are keyed in by users then I might be afraid of if they put in a comma since that would throw things off with any sort of looping.
<cfset lstFirstNames = "John,Bob,Tom,Jeff" />
<cfset lstLastNames = "Smith,Doe,Rodriguez,Horan" />
<cfloop list="#Variables.lstFirstNames#" index="FirstName" />
#FirstName# #ListGetAt(Variables.LastNames, ListFind(Variables.lstFirstNames, FirstName))#<br />
</cfloop>
try:
<cfset lstFirstNames = "John,Bob,Tom,Jeff" />
<cfset lstLastNames = "Smith,Doe,Rodriguez,Horan" />
<cfloop list="#Variables.lstFirstNames#" index="FirstName">
<cfoutput>#FirstName# #ListGetAt(Variables.lstLastNames, ListFind(Variables.lstFirstNames, FirstName))#</cfoutput><br />
</cfloop>