Related
I'm trying to add the serialized data in a request to third party API which needs a specific order of the data to be maintained, but SerializeJSON orders in alphabetical order which breaks the format required by the third party API. Could someone help me to figure it out
INPUT:
<cfset data ={
"Booking": {
"ActionCode":"DI",
"AgencyNumber":"23",
"Touroperator":"TVR",
"BookingNumber":"323",
},
"Payment": {
"__type":"paymenttype",
"PaymentProfile": {
"Value": 4,
"Manual": false
},
"PaymentType": 4,
"PaymentAction":2,
"Details": {
"IBAN": "DE02120300000000202051",
"BIC": "BYLADEM1001"
}
},
"Login":{
"UserCode": "usercode",
"Password": "password"
}
}>
When this method SerializeJSON() is used on my data:
SerializeJSON(data)
Current Output
"{"Booking":{"Touroperator":"TVR","ActionCode":"DI","BookingNumber":"323","AgencyNumber":"23"},"Login":{"UserCode":"usercode","Password":"password"},"Payment":{"PaymentProfile":{"Manual":false,"Value":4},"PaymentType":4,"PaymentAction":2,"__type":"paymenttype","Details":{"BIC":"BYLADEM1001","IBAN":"DE02120300000000202051"}}}"
Expected Output:
"{"Booking":{"ActionCode":"DI","AgencyNumber":"23","Touroperator":"TVR","BookingNumber":"323",},"Payment":{"__type":"paymenttype","PaymentProfile":{"Value":4,"Manual":false},"PaymentType":4,"PaymentAction":2,"Details":{"IBAN":"DE02120300000000202051","BIC":"BYLADEM1001"}},"Login":{"UserCode":"usercode","Password":"password"}}"
Structs in ColdFusion are unordered HashMaps, so there is no order at all. You can keep insertion order by using structNew("Ordered") (introduced with ColdFusion 2016). Unfortunately you can no longer use the literal syntax anymore, but I assume you are generating the data dynamically anyway.
<cfset data = structNew("Ordered")>
<cfset data["Booking"] = structNew("Ordered")>
<cfset data["Booking"]["ActionCode"] = "DI">
<cfset data["Booking"]["AgencyNumber"] = "TVR">
<cfset data["Booking"]["BookingNumber"] = "323">
<cfset data["Payment"] = structNew("Ordered")>
<cfset data["Payment"]["__type"] = "paymenttype">
<cfset data["Payment"]["PaymentProfile"] = structNew("Ordered")>
<cfset data["Payment"]["PaymentProfile"]["Value"] = 4>
<cfset data["Payment"]["PaymentProfile"]["Manual"] = false>
etc.
If you are stuck on an older ColdFusion version, you will have to use Java's LinkedHashMap.
<cfset data = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Booking"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Booking"]["ActionCode"] = "DI">
<cfset data["Booking"]["AgencyNumber"] = "TVR">
<cfset data["Booking"]["BookingNumber"] = "323">
<cfset data["Payment"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Payment"]["__type"] = "paymenttype">
<cfset data["Payment"]["PaymentProfile"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Payment"]["PaymentProfile"]["Value"] = 4>
<cfset data["Payment"]["PaymentProfile"]["Manual"] = false>
etc.
But be aware: LinkedHashMap is case-sensitive (and also type-sensitive: in case your keys are numbers, it does matter!).
<cfset data = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Test"] = "">
<!---
accessing data["Test"] = works
accessing data["test"] = doesn't work
accessing data.Test = doesn't work
--->
Another issue you might encounter: Due to ColdFusion's internal type casting, serializeJSON() might stringify numbers and booleans in an unintended way. Something like:
<cfset data = structNew("Ordered")>
<cfset data["myBoolean"] = true>
<cfset data["myInteger"] = 123>
could easily end up like:
{
"myBoolean": "YES",
"myInteger": 123.0
}
(Note: The above literal syntax would work perefectly fine, but if you are passing the values around as variables/arguments, casting eventually happens.)
The easiest workaround is explicitly casting the value before serializing:
<cfset data = structNew("Ordered")>
<cfset data["myBoolean"] = javaCast("boolean", true)>
<cfset data["myInteger"] = javaCast("int", 123)>
I'm trying to get the first and last day of the current quarter for the previous year. For some reason, the following code,
<cfset LastYear = dateAdd('yyyy', -1, now()) >
<cfset FirstDayOfQuarterLastYear = CreateDate(LastYear, (quarter-1)*3 + 1, 1) >
<cfset LastDayOfQuarterLastYear = DateAdd("d", -1, DateAdd("m", 3, FirstDayOfQuarterLastYear)) >
LastYear: #LastYear#<br>
FirstDayOfQuarterLastYear: #FirstDayOfQuarterLastYear#<br>
LastDayOfQuarterLastYear: #LastDayOfQuarterLastYear#<br>
It gives me
LastYear: {ts '2016-01-13 11:05:06'}
FirstDayOfQuarterLastYear: {ts '42382-01-01 00:00:00'}
LastDayOfQuarterLastYear: {ts '42382-03-31 00:00:00'}
You're using a date as the year parameter. You want to use the year only
<cfset FirstDayOfQuarterLastYear = CreateDate(year(LastYear), (quarter-1)*3 + 1, 1) >
This is my first post to SO, a resource that is incredibly valuable!
I am trying to determine if a value (state code) exists in a list of codes and if so, set a new variable that represents the division (ie. sales territory, no match = 15). The code below works, but I want to improve my skills and do this as efficiently as possible. So my question is there a "better" way to achieve the same result?
<cfset state = "LA">
<cfset division = 15>
<cfset position = 0>
<cfset aStates = ArrayNew(1)>
<cfset aStates[1] = "MA,ME,NH,RI,VT">
<cfset aStates[2] = "CT,DE,NJ,NY,DE">
<cfset aStates[3] = "DC,MD,VA,WV">
<cfset aStates[4] = "TN">
<cfset aStates[5] = "NC,SC">
<cfset aStates[6] = "GA">
<cfset aStates[7] = "FL">
<cfset aStates[8] = "AL,KY,LA,MS">
<cfset aStates[9] = "IL,WI">
<cfset aStates[10] = "CO,MN,ND,SD,WY">
<cfset aStates[11] = "IN,OH,MI">
<cfset aStates[12] = "ID,OR,UT,WA">
<cfset aStates[13] = "AZ,HI,NV">
<cfset aStates[14] = "CA">
<cfset position = 0>
<cfloop array="#aStates#" index="lStates">
<cfset position = position+1>
<cfif ListFindNoCase(lStates,variables.state) NEQ 0>
<cfset division = position>
</cfif>
</cfloop>
<cfdump var="#aStates#" label="states array">
<cfoutput>State: #state#</cfoutput>
<cfoutput>Division: #division#</cfoutput>
With the current structure, you are relying on string comparisons, so there is not too much room for improvement.
Unless there is a specific reason you must hard code the values, a more flexible approach is to store the information the database. Create tables to store the "states" and "divisions", and another table to store the relationships:
States
StateID | Code | Name
1 | ME | Maine
2 | GA | Georgia
3 | CA | California
4 | NH | New Hampshire
...
Divisions
DivisionID | Name
1 | Sales Territory
...
DivisionStates
DivisionID | StateID
1 | 1
1 | 4
6 | 2
14 | 3
...
Then use an OUTER JOIN to retrieve the selected state and division. Assuming #state# will always contain a valid entry, something along these lines (not tested):
SELECT s.Name AS StateName
, CASE WHEN d.Name IS NULL THEN 'No Match' ELSE d.Name END AS DivisionName
FROM States s
LEFT JOIN DivisionStates ds ON ds.StateID = s.StateID
LEFT JOIN Divisions d ON d.DivisionID = ds.DivisionID
WHERE s.Code = <cfqueryparam value="#state#" cfsqltype="cf_sql_varchar">
I do not know the source of the #state# variable, but I am guessing it may be passed via a form <select>. If it is currently hard coded, you could simplify things further by using querying the "states" table, and using it to populate the list. Also, consider using the ID as the list value, rather than the state "code".
I thought of an approach in this by placing the states into a Struct; with the state codes as keys.
<cfscript>
state = "LA";
division = 15;
states_divisions_map = {
MA: 1, ME: 1, NH: 1, RI: 1, VT: 1,
CT: 2, NJ: 2, NY: 2, DE: 2,
DC: 3, MD: 3, VA: 3, WV: 3,
TN: 4,
NC: 5, SC: 5,
GA: 6,
FL: 7,
AL: 8, KY: 8, LA: 8, MS: 8,
IL: 9, WI: 9,
CO: 10, MN: 10, ND: 10, SD: 10, WY: 10,
IN: 11, OH: 11, MI: 11,
ID: 12, OR: 12, UT: 12, WA: 12,
AZ: 13, HI: 13, NV: 13,
CA: 14
};
if(StructKeyExists (states_divisions_map, state) ){
division = states_divisions_map[state];
}
writeDump(var: states_divisions_map, label: "States");
writeOutput("State: #state#");
writeOutput("Division: #division#");
</cfscript>
This way you can quickly check for the state without all the loops.
Observation: In your original code, DE is present twice.
ListContains() is like ListFind(), but it searches for a list element that contains the the text you're looking for anywhere in it. (ListFind() looks for a list element that entirely matches the string. Generally ListFind() is the better tool but not in this case.
(CF does have an ArrayContains, but it doesn't work for this task, it merely tells you if the array contains an exactly matched element.)
Because you're searching for unique two-char state-codes, this is a perfect use of the function.
<cfset aStates = ArrayNew(1)>
<cfset aStates[1] = "MA,ME,NH,RI,VT">
<cfset aStates[2] = "CT,DE,NJ,NY,DE">
<cfset aStates[3] = "DC,MD,VA,WV">
<cfset aStates[4] = "TN">
<cfset aStates[5] = "NC,SC">
<cfset aStates[6] = "GA">
<cfset aStates[7] = "FL">
<cfset aStates[8] = "AL,KY,LA,MS">
<cfset aStates[9] = "IL,WI">
<cfset aStates[10] = "CO,MN,ND,SD,WY">
<cfset aStates[11] = "IN,OH,MI">
<cfset aStates[12] = "ID,OR,UT,WA">
<cfset aStates[13] = "AZ,HI,NV">
<cfset aStates[14] = "CA">
<cfset lstates = arraytolist(astates,"|")>
<cfset division = listcontainsnocase(lstates,"CO","|")>
<cfoutput>Division: #division#<br>
State: #state#</cfoutput>
We set the delimiter to | (pipe), but you can use anything, other than the default (comma), because you're using that for you're sub-delimiter.
For future reference, if you're using CF 9 (I believe) or after, you can use array shorthand for quickly building arrays.
<cfset aStates=["MA,ME,NH,RI,VT","CT,DE,NJ,NV,DE","DC,MD,VA,WV"]> ...
While it may seem merely like a stylistic difference, it saves a lot of time.
Structures can be created similarly.
<cfset MyDogIs = {Size = "medium", Color = "Black", HasPaws = ["FL","FR","BL","BR"]}>
(And you can nest implicit array and structs within another!)
Thank you all for your input. I am answering this question myself with the code I decided to use since it is based on piece supplied by multiple answers/comments. I hope this is the correct way to do this, if not please advise.
The divisions were not stored in the db as they are a set it and forget it mapping that has not changed in years and I thought I'd save a call to the DB. If they were subject to change I would have taken a table approach suggested by #leigh
Thanks to #matt-busche for the improved loop, ucase() and break recommendations, and #cfqueryparam for the short hand array suggestion.
Here's what I went with. The code is actually a function in a cfc that processes a form submission but I have pasted only the relevant part.
<cfset form.division = 15>
<cfset aDivisions = ["MA,ME,NH,RI,VT",
"CT,DE,NJ,NY,DE",
"DC,MD,VA,WV",
"TN",
"NC,SC",
"GA",
"FL",
"AL,KY,LA,MS",
"IL,WI",
"CO,MN,ND,SD,WY",
"IN,OH,MI",
"ID,OR,UT,WA",
"AZ,HI,NV",
"CA"]>
<cfloop from="1" to="#ArrayLen(aDivisions)#" index="i">
<cfif ListFind(aDivisions[i],Ucase(arguments.sForm.state)) NEQ 0>
<cfset form.division = i>
<cfbreak>
</cfif>
</cfloop>
I want to change the DayOfWeek() function so that I can get dates according to my own desired first day of the week. So what I am doing in this code is that I am setting a startDate, endDate and selectDays that I want to check. Here is my code:
<cfscript>
function Mydayofweek(date, day_1)
{
return (((DayOfWeek(date) + (7 -day_1)) MOD 7) +1);
}
</cfscript>
<cfset startDate = '07/01/2013'>
<cfset endDate = '07/25/2013'>
<Cfset mydates = ''>
<cfset selectDays = '2,6'>
<cfset MyWeekFirstDay = 6> <!---I selected Friday = 6 --->
<cfset new = ''>
<cfoutput>
<cfloop list="#selectDays#" delimiters="," index="d">
<cfset new &= '#Mydayofweek(d, MyWeekFirstDay)#,' >
</cfloop>
<cfif new NEQ ''>
<cfset ScheduleDate = left(new, (len(new)-1) )>
</cfif>
<cfdump var="#ScheduleDate#"><br />
</cfoutput>
<cfset AppendToMyDates = false>
<cfloop from="#startDate#" to="#endDate#" index="day">
<cfif AppendToMyDates is false and DayOfWeek(day) is ListFirst(selectDays)>
<cfset AppendToMyDates = true>
</cfif>
<cfif listfind(ScheduleDate, DayOfWeek(day), ',') NEQ 0 and AppendToMyDates is true>
<cfset mydates &= "#dateformat(day, 'mmm, dd, yyyy dddd')#,<br />">
</cfif>
</cfloop><cfoutput>#mydates#</cfoutput>
This is written in ColdFusion. That code generates this output:
4,1
Jul, 03, 2013 Wednesday,
Jul, 07, 2013 Sunday,
Jul, 10, 2013 Wednesday,
Jul, 14, 2013 Sunday,
Jul, 17, 2013 Wednesday,
Jul, 21, 2013 Sunday,
Jul, 24, 2013 Wednesday,
The Output Should be like this because i select Friday = 1 to Thursday = 7 so the above days selectDays = '2,6' should now point to selectDays = '7,4' with respect to my first days 2,6
7,4
Jul, 06, 2013 Saturday,
Jul, 10, 2013 Wednesday,
Jul, 13, 2013 Saturday,
Jul, 17, 2013 Wednesday,
Jul, 20, 2013 Saturday,
Jul, 24, 2013 Wednesday,
I have set my selectDays = '2,6' it means I want to get dates of Saturday and Wednesday because I have set 6 as my week first day and it start from Friday (by default it was sunday). My days start from Sunday Sunday = 1 , Monday = 2 , Tuesday = 3 , Wednesday = 4 , Thursday = 5 , Friday = 6 , Saturday = 7 and now I changed my date start from Friday = 1 , Saturday = 2 ,Sunday = 3 , Monday = 4 , Tuesday = 5 , Wednesday = 6 , Thursday = 7 in script function. Actually I think there is error in my script function that i don't understand. Please help me out to find the problem and it's solution thanks
UPDATED
You just need to call your script into your loop. No need to make any function just change the code like this one.
Copy this code hope this will solve your problem.
<cfscript>
function Mydayofweek(date, day_1)
{
return (((DayOfWeek(date) + (7 -day_1)) MOD 7) +1);
}
</cfscript>
<cfset startDate = '07/01/2013'>
<cfset endDate = '07/25/2013'>
<Cfset mydates = ''>
<cfset selectDays = '2,6'>
<cfset MyWeekFirstDay = 6><!---I selected Friday = 6 --->
<cfloop from="#startDate#" to="#endDate#" index="day">
<cfif listfind(selectDays, Mydayofweek(day,MyWeekFirstDay), ',') NEQ 0 >
<cfset mydates &= "#dateformat(day, 'mmm, dd, yyyy dddd')#,<br />">
</cfif>
</cfloop>
<cfoutput>#mydates#</cfoutput>
With the new information that selectDays might not always be the same, I'd do something like this:
<cfset AppendToMyDates = false>
<cfloop from="#startDate#" to="#endDate#" index="day">
<cfif AppendToMyDates is false and DayOfWeek(day) is ListFirst(SelectDays)>
<cfset AppendToMyDates = true>
</cfif>
<cfif listfind(selectDays, DayOfWeek(day), ',') NEQ 0 and AppendToMyDates is true>
<cfset mydates &= "#dateformat(day, 'mmm, dd, yyyy dddd')#,<br />">
</cfif>
</cfloop>
Edit starts here
If you want the start of the week to be a variable, you want to write your own version of DayOfWeek() with a different name. The structure would be something like this:
<cffunction name="DayOfWeekModified returntype="numeric">
<cfargument name="WeekStartsOn" type="numeric" required="yes">
<cfscript>
var DayNumber = 0;
code to generate it based on arguments.WeekStartsOn
return DayNumber;
<cfscript>
<cffunction>
You then call this function instead of DayOfWeek() in your loop.
The issue you are having is with your cfif condition. You are looping over the dates and then checking if the given date is a Wednesday or Friday and the results you are getting are correct. Since you want to start with a Friday (ignoring the first Wednesday) you need to add that to your code. This may work for you:
<cfset startDate = '06/11/2013'>
<cfset endDate = '06/25/2013'>
<cfset mydates = ''>
<cfset selectDays = '6,4'>
<cfloop from="#startDate#" to="#endDate#" index="day">
<cfif listfind(selectDays, DayOfWeek(day), ',') NEQ 0>
<cfif mydates NEQ "" OR DayOfWeek(day) EQ "6">
<cfset mydates &= "#dateformat(day, 'mmm, dd, yyyy dddd')#,<br />">
</cfif>
</cfif>
</cfloop>
<cfoutput>#mydates#</cfoutput>
I added an additional cfif condition around the setting of mydates. This code mydates NEQ "" is checking to see if mydates is not empty, meaning we have already satisified the next condition. If mydates is empty then also check to see if the given date is a Friday with DayOfWeek(day) EQ "6". This should guarantee that the first date entered in mydates is a Friday.
Not sure how I feel about this code but it seemed to work for me with your example.
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>