I'm passing a structure to a function for req. fields validation but I first check whether or not my structure is empty.
If all elements in my structure is empty (emptry string), I don't pass this structure to for validation.
I used StructIsEmpty to check my structure. The problem is, when my Structure's elements contain only empty string, StructIsEmpty return NO. Unfortunately I'm still on CF8.
How can I have StructIsEmpty to return YES when all of the structure elements only has empty string?
<cfset st_MyStruct=StructNew()>
<cfset st_MyStruct["InstType"]="#Trim(arr[112])#">
<cfset st_MyStruct["InstId"]="#Trim(arr[113])#">
<cfset st_MyStruct["PLN"]="#Trim(arr[115])#">
<cfset st_MyStruct["PFN"]="#Trim(arr[116])#">
<cfset st_MyStruct["Referal"]="#Trim(arr[118])#">
cfif StructIsEmpty(st_MyStruct) NEQ "NO">
<CFINVOKE component="cfcomponents.ValidateFields" method="CheckReqFields"
st_MyStruct="#st_MyStruct#"
Inst="#arguments.Inst#" >
</cfif>
Like Dan said, this struct is not empty. If you want to check if your struct has values that are blank you can do something like this. And check if your structFieldsAreEmpty variables true, if it does then your structure has all blank values. If your struct returns more than one entry you would need to modify this code
<cfset st_MyStruct = {}>
<cfset st_MyStruct["InstType"] = ''>
<cfset st_MyStruct["InstId"] = ''>
<cfset st_MyStruct["PLN"] = ''>
<cfset st_MyStruct["PFN"] = ''>
<cfset st_MyStruct["Referal"] = ''>
<cfset structFieldsAreEmpty = checkStructValuesEmpty(st_MyStruct) />
<cffunction name="checkStructValuesEmpty" access="private" returntype="boolean" output="false">
<cfargument name="myStruct" type="struct" required="true">
<cfloop collection="#arguments.myStruct#" index="i">
<cfif len(trim(arguments.myStruct[i]))>
<cfreturn false>
</cfif>
</cfloop>
<cfreturn true>
</cffunction>
If you want to do this in a single line, you could serialize the struct to JSON and search that using regex for any non-empty string values, like so:
structIsEmptyStrings = refind(':("[^"]+"|\d+|true|false)', serializeJSON(st_MyStruct)) == 0;
That regex is looking for any values that are either not empty strings (""), numeric, or a boolean value (true or false). Keep in mind that this will not be accurate if any values in the struct are any types other than string, numeric, or boolean (nested arrays or structs will not be checked). Also, if any string values are only spaces, this will consider the struct to be not empty (which might not be what you're looking for).
Related
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>
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#" >
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>
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>
I'm looking to learn how to create a REGEX in Coldfusion that will scan through a large item of html text and create a list of items.
The items I want are contained between the following
<span class="findme">The Goods</span>
Thanks for any tips to get this going.
You don't say what version of CF. Since v8 you can use REMatch to get an array
results = REMatch('(?i)<span[^>]+class="findme"[^>]*>(.+?)</span>', text)
Use ArrayToList to turn that into a list.
For older version use REFindNoCase and use Mid() to extract substrings.
EDIT: To answer your follow-up comment the process of using REFind to return all matches is quite involved because the function only returns the FIRST match. This means you actually have to call REFind many times passing a new startpos each time. Ben Forta has written a UDF which does exactly this and will save you some time.
<!---
Returns all the matches of a regular expression within a string.
NOTE: Updated to allow subexpression selection (rather than whole match)
#param regex Regular expression. (Required)
#param text String to search. (Required)
#param subexnum Sub-expression to extract (Optional)
#return Returns a structure.
#author Ben Forta (ben#forta.com)
#version 1, July 15, 2005
--->
<cffunction name="reFindAll" output="true" returnType="struct">
<cfargument name="regex" type="string" required="yes">
<cfargument name="text" type="string" required="yes">
<cfargument name="subexnum" type="numeric" default="1">
<!--- Define local variables --->
<cfset var results=structNew()>
<cfset var pos=1>
<cfset var subex="">
<cfset var done=false>
<!--- Initialize results structure --->
<cfset results.len=arraynew(1)>
<cfset results.pos=arraynew(1)>
<!--- Loop through text --->
<cfloop condition="not done">
<!--- Perform search --->
<cfset subex=reFind(arguments.regex, arguments.text, pos, true)>
<!--- Anything matched? --->
<cfif subex.len[1] is 0>
<!--- Nothing found, outta here --->
<cfset done=true>
<cfelse>
<!--- Got one, add to arrays --->
<cfset arrayappend(results.len, subex.len[arguments.subexnum])>
<cfset arrayappend(results.pos, subex.pos[arguments.subexnum])>
<!--- Reposition start point --->
<cfset pos=subex.pos[1]+subex.len[1]>
</cfif>
</cfloop>
<!--- If no matches, add 0 to both arrays --->
<cfif arraylen(results.len) is 0>
<cfset arrayappend(results.len, 0)>
<cfset arrayappend(results.pos, 0)>
</cfif>
<!--- and return results --->
<cfreturn results>
</cffunction>
This gives you the start (pos) and length of each match so to get each substring use another loop
<cfset text = '<span class="findme">The Goods</span><span class="findme">More Goods</span>' />
<cfset pattern = '(?i)<span[^>]+class="findme"[^>]*>(.+?)</span>' />
<cfset results = reFindAll(pattern, text, 2) />
<cfloop index="i" from="1" to="#ArrayLen(results.pos)#">
<cfoutput>match #i#: #Mid(text, results.pos[i], results.len[i])#<br></cfoutput>
</cfloop>
EDIT: Updated reFindAll with subexnum argument. Setting this to 2 will capture the first subexpression. The default value 1 captures the entire match.
Try looking into the possibility of making your HTML work with a regular DOM Parser and querying it via XPath instead of hammering this trough an regex-based abomination.
to make HTML input usable, pass it through jTidy (see http://jtidy.riaforge.org/)
Once you have well-formed XML/XHTML, build an XML document from it
<cfset dom = XmlParse(scrubbedHtml, true)>
query the XML document using XPath
<cfset result = XmlSearch(dom, "//span[#class='findme']")>
Done.
EDIT: Coldfusion's XmlSearch() doesn't have great XML namespace support. If you end up producing XHTML instead of the more recommendable XML, use the following XPath (note the colon) "//:span[#class='findme']" or "//*:span[#class='findme']". See here and here for more info.
See the jTidy API documentation for a complete overview what jTidy can do.