Convert name value pairs to struct - web-services

It's been awhile since I've written this type of ColdFusion code, hence the question.
I am returning values from a .NET webservice into ColdFusion. The webservice returns an array of strings. The following code...
<cfoutput>
<cfset xArrayItems=#GetRequestedUserSettings.settingValues.getString()#>
<cfset xLen=ArrayLen(GetRequestedUserSettings.settingValues.getString())>
<cfloop index="x" from=1 to="#xLen#">
#xArrayItems[x]#<br />
</cfloop>
</cfoutput>
results in the following output ...
maxsize=50
isdomainadmin=False
seenwelcome=False
I want to put those name/value pairs into a meaningful structure so that I can reference them farther down in the code. I actually need to pass them in as a cfinvokearguments for the next webservice call.
Could someone please be kind enough to remind me how to do this in CF8? Most of what I am finding refers to newer versions.

I ended up with something quite similar to what #Henry you provided.
<cfset UserSettings = structNew()>
<cfset xArrayItems= GetRequestedUserSettings.settingValues.getString()>
<cfset xLen=ArrayLen(GetRequestedUserSettings.settingValues.getString())>
<cfloop index="x" from=1 to="#xLen#">
<cfset varName = ListGetAt(xArrayItems[x], 1, "=")>
<cfset varValue = ListGetAt(xArrayItems[x], 2, "=")>
<cfset "UserSettings.#varname#" = varValue>
</cfloop>
Not sure if an Array or a Struct is a better solution, but the both work in the end.

Slightly more readable version I can come up with that will work with CF8:
<cfset UserSettings = {}>
<cfset xArrayItems = GetRequestedUserSettings.settingValues.getString()>
<cfloop array="#xArrayItems#" index="item">
<cfset varname = ListFirst(item, "=")>
<cfset varvalue = ListRest(item, "=")>
<cfset UserSettings[varname] = varvalue>
</cfloop>

Related

Coldfusion 10 cfloop errors

I am getting an error after upgrade from coldfusionOX to coldfusion 10.
Error Occurred While Processing Request 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.
It occurs at line " cfloop index="local.thisRight" list="#rights#" ". It seems like ColdFusion does not like the "rights" here.
Anyone can give me some help? Thanks so much.
<cfif local.profile.rights.profile.self is not "">
<cfquery name="local.getAffiliations" datasource="#Request.readerDSN#">
SELECT tblPersonsToAffiliations.affiliationID, tblPersonsToAffiliations.rights, tblAffiliations.affiliationType, tblPersonsToAffiliations.relationshipType
FROM tblPersonsToAffiliations INNER JOIN tblAffiliations
ON tblPersonsToAffiliations.affiliationID = tblAffiliations.affiliationID
WHERE tblPersonsToAffiliations.personID IN (#local.profile.rights.profile.self#)
AND (tblPersonsToAffiliations.archived IS NULL
OR tblPersonsToAffiliations.archived = '')
</cfquery>
<cfif local.getAffiliations.recordCount is not 0>
<cfloop query="local.getAffiliations">
<cfif local.getAffiliations.relationshipType is "interested">
<cfset local.thisRelationshipType = "provisionalMember">
<cfif IsDefined("local.profile.rights.#affiliationType#.#local.thisRelationshipType#")>
<cfset local.profile.rights[affiliationType][local.thisRelationshipType] = ListAppend(local.profile.rights[affiliationType][local.thisRelationshipType], affiliationID)>
<cfelse>
<cfset local.profile.rights[affiliationType][thisRelationshipType] = affiliationID>
</cfif>
<cfelse>
<cfset local.thisRelationshipType = "fullMember">
<cfif IsDefined("local.profile.rights.#affiliationType#.#local.thisRelationshipType#")>
<cfset local.profile.rights[affiliationType][local.thisRelationshipType] = ListAppend(local.profile.rights[affiliationType][local.thisRelationshipType], affiliationID)>
<cfelse>
<cfset local.profile.rights[affiliationType][local.thisRelationshipType] = affiliationID>
</cfif>
<cfif isNull(rights)>
<cfelse>
<cfloop index="local.thisRight" list="#rights#" >
<cfif IsDefined("local.profile.rights.#affiliationType#.#local.thisRight#")>
<cfset local.profile.rights[affiliationType][local.thisRight] = ListAppend(local.profile.rights[affiliationType][local.thisRight], affiliationID)>
<cfelse>
<cfset local.profile.rights[affiliationType][local.thisRight] = affiliationID>
</cfif>
</cfloop>
</cfif>
</cfif>
</cfloop>
</cfif>
</cfif>
A bit earlier in your code you do this:
<cfif local.getAffiliations.relationshipType is "interested">
I think you need the same query name prefix in front of "rights" that is used when evaluating "relationshipType".
Try this:
#local.getAffiliations.rights#
HTH!
I am betting it is failing on this line:
<cfloop index="local.thisRight" list="rights" >
You are attempting to use the string "rights" as a list. My first reaction would be that you need to make that:
<cfloop index="local.thisRight" list="#rights#" >

How to extract substring from a string in ColdFusion?

I have a string like:
oauth_token=xxxxxxxxxxx&oauth_token_secret=xxxxxxxxxxx&oauth_callback_confirmed=true
I want to extract these values into three variables.
Can you please suggest the easiest method in ColdFusion?
#Dan Bracuk was close, this will do what you want.
<cfset myString = "oauth_token=xxxxxxxxxxx&oauth_token_secret=xxxxxxxxxxx&oauth_callback_confirmed=true">
<cfloop list="#myString#" index="pair" delimiters="&">
<cfset myStruct[ListFirst(pair, "=")] = ListLast(pair, "=")>
</cfloop>
<cfdump var="#myStruct#">
I would try something like this:
<cfloop
list = "oauth_token=xxxxxxx&oauth_token_secret=xxxxx&oauth_callback_confirmed=true"
index="pair" delimiter="&">
<cfset ListFirst(pair, "=") = ListLast(pair, "=")>
</cfloop>
I'm not sure if it work, but it would be worth a shot.
There is another alternative to the accepted solution:
<cfset t = "oauth_token=abc&oauth_token_secret=def&oauth_callback_confirmed=true">
<cfset oauth_token = ListGetAt(ListGetAt(t,1,"&"),2,"=")>
<cfset oauth_token_secret = ListGetAt(ListGetAt(t,2,"&"),2,"=")>
<cfset oauth_callback_confirmed = ListGetAt(ListGetAt(t,3,"&"),2,"=")>

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 - How to loop over all contents in a list

Making the array:
<cfset tempArr = DeserializeJSON(URL.data) />
<cfset temp1 = "" />
<cfset temp2 = "" />
<cfset selectList1 = "" />
<cfset selectList2 = "" />
<cfloop array=#tempArr# index="i">
<cfset temp1 = GetToken(i,1,":")>
<cfset temp2 = GetToken(i,2,":")>
<cfset selectList1 = listAppend(selectList1, temp1)>
<cfset selectList2 = listAppend(selectList2, temp2)>
</cfloop>
Looping through it??:
<cfquery name="sample" datasource="database">
SELECT *
FROM table
WHERE
<cfloop from="1" to="#listLen(selectList1)#" index="i"/>
#ListGetAt(selectList1, i)# = <cfqueryparam value="#ListGetAt(selectList2)#" />
</cfloop>
<cfif i neq listLen(#selectList1#)>
AND
</cfif>
</cfquery>
My intention is to search dynamically in a table based on the array that was received from the javascript page. The data comes in this form -> columnName:searchBy. ie, a sample piece would be name:Jim. I would like to build in dynamic code that would allow me to search by different columns but I can't get my loop to work. I get this error if it helps:
(Invalid CFML construct found on line 20 at column 59.)
which is this line:
<cfloop from="1" to="#listLen(selectList1)#" index="i" />
Kyle's answer is 100% correct, but here's a better solution. Using lists is a very inefficient process (see below) and using listGetAt will only anger future programmers. You can use an array to house the data and use a default WHERE statement to simplify your looping.
<cfset tempArr = DeserializeJSON(URL.data) />
<cfset temp1 = "" />
<cfset temp2 = "" />
<cfset selectList1 = [] />
<cfset selectList2 = [] />
<cfloop array=#tempArr# index="i">
<cfset temp1 = GetToken(i,1,":")>
<cfset temp2 = GetToken(i,2,":")>
<cfset arrayAppend(selectList1, temp1)>
<cfset arrayAppend(selectList2, temp2)>
</cfloop>
<cfif NOT arrayIsEmpty(tempArr)>
<cfquery name="sample" datasource="database">
SELECT column1, column2, column3
FROM table
WHERE 1 = 1
<cfloop from="1" to="#listLen(selectList1)#" index="i"/>
AND #selectList1[i]# = <cfqueryparam value="#selectList2[i]#" />
</cfloop>
</cfquery>
</cfif>
When you append to a list a new string is created in memory that combines the two previous string and the previous string is deleted. This is definitely premature optimization, but it's still a good practice to avoid using lists especially when you need to access elements in them.
I think your problem might be that the cfloop tag is self closing. Try this instead:
<cfloop from="1" to="#listLen(selectList1)#" index="i">
list attribute can be used in <cfloop>.
<cfloop list="#selectList1#" index="i">
#i# <!--- list element can be processed here with variable name #i# --->
</cfloop>

Are the following two code blocks logically equivalent?

Is there a logical difference between the following two blocks? And is there one form more correct than the other? They would both reside in their own function--something I omitted here.
<cfset local.result = 1 />
<cfset local.i = 1 />
<cfloop from="1" to="5" index="i">
<cfset result = result * i />
</cfloop>
And
<cfset local.result = 1 />
<cfset local.i = 1 />
<cfloop from="1" to="5" index="i">
<cfset local.result = local.result * local.i />
</cfloop>
Yes. In your second example you are making the exact same result; however, you have improved readability by explicitly identifying the scope you intend to modify--which is a good thing.
ColdFusion, will first search the LOCAL scope, so, you have not saved ColdFusion much processing; however, the code is cleaner now. If result existed in the CLIENT or COOKIE scope you would have saved ColdFusion the work of having to first evaluate four or five other scopes.
I once used to use the 'var result = 0;' style of localizing variables to a function, but, I now explicitly identify all my scopes to help ensure I have correctly scoped all variables and make the code easier to understand for others.
To summarize, the code is exactly the same to the machine but is now easier to understand for the human.
One suggestion... change:
<cfset local.i = 1 />
<cfloop from="1" to="5" index="i">
to
<cfloop from="1" to="5" index="local.i">
one less line of code, even more clear what's going on.