From a best-practice or performance standpoint which option is best to use for testing if a FORM value is not blank?
<cfif NOT isNull(FORM.Forename)>
OR
<cfif Len(Trim(FORM.Forename)) GT 0>
OR
<cfif FORM.Forename NEQ "">
I don't want the value to be valid if it has something silly like 4 blank spaces in it. I guess 4 blank spaces is not technically a NULL value?
The second
<cfif Len(Trim(FORM.Forename)) GT 0>
The first will not be null. Cf will receive an empty string or no form element.
The third is covered by the second.
You may need to wrap the form element with an isdefined depending on the form element type.
The problem with all of these examples is that the variable needs to exist or it errors (hence Kobby's "isdefined").
To get around missing FORM variables, attributes, etc. (and not have to isDefined test for them all of the time) I constantly use these custom "NULL" functions...
/* "NULL" functions are to assist with setting and returning variable values of the type you want,
whether they already exist or not ... for easy coding.
<cfif NULL("message") EQ "Optional message value" />
#message# now exists <cfif message EQ "Other value" />
<cfif boolNULL("form.optionalVariableName") />
#form.optionalVariableName# now exists as a boolean <cfif form.optionalVariableName />
<cfif valNULL("attributes.expectedNumericVariableName") />
#attributes.expectedNumericVariableName# now exists as a numeric <cfif attributes.expectedNumericVariableName GT 5 />
<cfif lenNULL("query.expectedNonBlankQueryOrStructureVariableName") />
*/
// List not last is all elements except the last
function listNotLast(list) {
// 2nd param is optional list delimiter
var delimiter = arrayLen(arguments) GT 1 ? arguments[2] : ",";
// If more than 1 element return the list without the last one
return listLen(list, delimiter) GT 1 ? listDeleteAt(list, listLen(list, delimiter), delimiter) : list;
}
// Sets and returns a string even if not defined (sets the variable to "" or a query as query.column[1] = "").
function NULL(v) {
// Override the default blank string with optional 2nd param
var NA = arrayLen(arguments) GT 1 ? arguments[2] : "";
// Variable v does not exist. We'll make it an empty string
if (!isDefined(v)) {
// Query vars are treated differently. Can fail if we're a struct. It looks like we want a query variable query.column_name
if (listLen(v, ".") GT 1 AND !listFindNoCase("caller,attributes,request,client,session,variables,cookie,cgi", listFirst(v, "."))) {
// If the query doesn't exist at all make a blank one (NA)
if (!isDefined(listFirst(v, "."))) {
setVariable(listFirst(v, "."), queryNew("NULL"));
}
// Now add the column name we want as a blank array (NA)
queryAddColumn(evaluate(listFirst(v, ".")), listLast(v, "."), [ NA ]);
}
// Non-query variables created here as a blank string (NA)
else setVariable(v, NA);
}
// Originally defined or not we just return our string value
return evaluate(v);
}
// Returns a numeric value even if not defined (sets the variable to 0). Override the set and return in second var. Uses the NULL and listNotLast functions.
function valNULL(v) {
if (!isDefined("NULL") OR !isDefined("listNotLast")) throw(type = "Missing function", message = "The NULL and listNotLast functions are required for the valNULL function.");
// Return a zero if non defined or non-numeric. Override here with 2nd param
var NA = arrayLen(arguments) GT 1 ? arguments[2] : 0;
var LE = listNotLast(v, ".")
// Use the NULL routine to ensure we're already created, test numericy
if (!isNumeric(NULL(v))) {
// We're non-numeric, check if its a query var (NULL will have created if need be)
if (isDefined(LE) AND isQuery(evaluate(LE))) {
// The query may need a row
if (!evaluate(LE & ".recordCount")) queryAddRow(evaluate(LE), 1);
// NULL already returned us a blank query with our column name if it didn't exist, just set our cell's NA (0) value here
querySetCell(evaluate(LE), listLast(v, "."), NA, 1);
// Non-query variables just set NA (0) value
} else setVariable(v, NA);
}
// Originally defined or not we just return our numeric value
return evaluate(v);
}
// Returns a boolean value even if not defined (sets the variable to false). Uses the NULL function. No overrides.
function boolNULL(v) {
if (!isDefined("NULL")) throw(type = "Missing function", message = "The NULL function is required for the boolNULL function.");
// Use the NULL function to return true for "1" values
if (NULL(v) EQ 1) setVariable(v, true);
// If its not boolean then its false. eg. 0, [blank], false, 'fasle' all set and return false
if (NOT isBoolean(NULL(v))) setVariable(v, false);
// Boolean only values returned here
return evaluate(v);
}
// Returns a string length even if not defined (sets the variable to "" and returns 0 length). Uses the NULL function. No overrides.
function lenNULL(v) {
if (!isDefined("NULL")) throw(type = "Missing function", message = "The NULL function is required for the lenNULL function.");
return len(NULL(v));
}
Related
working on the data returned by code
Trying to add some logic that if the value exists, show it else put empty
<cfset myStruct = {
"access_token" : "#st.access_token#",
"id": "#res.names[1].metadata.source.id#",
"name" : "#isDefined('res.names') ? res.names[1].displayname : ''#",
"other" : {
"email" : "#res.emailAddresses[1].value#"
}
}>
Open in new window
its not clean and it throws error on line 3 which is ID, so what kind of isDefined or structkeyexists i can write if it exists add it, else put an empty value
You could try Elvis operator
Edit: Unless you really need the values as a string, you do not need to use pounds to output the values
Edit 2: Have updated the example to use the right comment
<cfset myStruct = {
"access_token" : "#st.access_token#" <!--- If you have numeric token and need it to be a string --->
, "id" : res.names[ 1 ].metadata.source.id ?: ""
, "name" : res.names[ 1 ].displayname ?: ""
, "other" : {
"email" : res.emailAddresses[ 1 ].value ?: ""
}
}>
I have a list like:
<cfset list ="group 1:1; group 2:4; group a:7; group 1:3; group a:1;">
What I want to do is count (sum up) how many people are in group 1, group 2, group a, etc.. Now these fields are dynamic, so I don't know the exact name of the groups.
So the end result in this example is:
group 1: 4
group 2: 4
group a: 8
So the list is just an example, in reality it's much bigger with more groups (dynamic names). I'm using Coldfusion/Lucee. Can someone help me with this?
Just one of numerous of possible alternative ways of doing this. Starting from using a query instead of a list as the starting value.
What I am doing is to loop the list with ; as delimiter and adding the values to a structure. I later use that structure to loop and list the final total.
<cfset list ="group 1:1; group 2:4; group a:7; group 1:3; group a:1;">
<cfset totalStruct = {}>
<cfloop list="#list#" item="group" delimiters=';'>
<cfset groupName = listFirst(group, ':')>
<cfset groupNameKey = replace(groupName, ' ', '', 'all')>
<cfset groupValue = val(listLast(group, ':'))>
<cfif !structKeyExists(totalStruct, groupNameKey)>
<cfset totalStruct[groupNameKey] = {name:groupName, total=groupValue}>
<cfelse>
<cfset totalStruct[groupNameKey].total += groupValue>
</cfif>
</cfloop>
<cfoutput>
<ul>
<cfloop collection="#totalStruct#" item="group">
<li>#totalStruct[group].name# : #totalStruct[group].total#</li>
</cfloop>
</ul>
</cfoutput>
DEMO
Another alternative is to use the map and/or reduce closure functions to parse your list.
NOTE 1: I generally find cfscript to be much easier for most things for parsing text or "looping".
NOTE 2: I'm not a huge fan of loops, especially around large text values. Closure functions will likely be much more performant.
<cfset lst ="group 1:1; group 2:4; group a:7; group 1:3; group a:1;">
First, with a map() inside of a function:
<cfscript>
public Struct function parseLst (required String lst) {
var retval = {} ; //// Default return variable.
//// https://docs.lucee.org/reference/functions/listmap.html
arguments.lst.listmap(
function(el) {
var k = el.listFirst(":").ltrim() ; /// Get the "key" and strip extra leading space.
var v = el.listLast(":") ; /// Get the "value".
var p = retval["#k#"]?:0 ; /// Use Elvis to default to 0 if no struct Key exists.
retval["#k#"] = v + p ; /// Set the value of the key. NOTE: A struck key with the same name will generally overwrite itself. We want to add it.
}
,";" /// Specify the delimiter of the list.
) ;
return retval ;
}
writeDump(parseLst(lst));
</cfscript>
Then with a reduce() without being inside a function.
<cfscript>
//// https://docs.lucee.org/reference/functions/listreduce.html
r = listReduce(lst,
function(prev,nxt){
k = nxt.listFirst(":").ltrim() ; /// Get the "key" and strip extra leading space.
/// To shorten it, I just skipped setting the value beforehand and just did it while setting the struct value. Same method as above.
prev["#k#"] = (nxt.listLast(":"))+(prev["#k#"]?:0) ;
return prev ;
}
,
{} // Initial value
,";" // Delimiter
) ;
writedump(r) ;
</cfscript>
Both could (and probably should) be inside a function, and then you could just send your list variable to it.
And if possible, it might be a lot easier to fix the original list to be simpler to work with.
https://trycf.com/gist/dda51d88504a625fce5548142d73edb3/lucee5?theme=monokai
=======================================================
EDIT:
An alternative to using listFirst/Last() functions would be to just convert the "list" to an array and then use those pieces to get your "key" and "value".
<cfscript>
//// https://docs.lucee.org/reference/functions/listreduce.html
s = listReduce(lst,
function(prev,nxt){
var elem = nxt.listToArray(":") ;
prev["#elem[1].ltrim()#"] = elem[2] + (prev["#elem[1].ltrim()#"]?:0) ;
return prev ;
}
,
{} // Initial value
,";" // Delimiter
) ;
writedump(s) ;
</cfscript>
With the url string below, I need to find the value of the parameter named construction.
<cfset understand = "http://www.example.com/ops.cfm?id=code&construction=148&construction=150&Building=852&Building=665&Building=348&Building=619&Building=625&Building=626&_=1426353166006&action=SUBMIT">
<cfset understand2 = "http://www.example.com/ops.cfm?id=code&construction=AVENT+Construction+Site&construction=Signore+upper+constructions&Building=852&Building=665&Building=348&Building=619&Building=625&Building=626&_=1426353166006&action=SUBMIT">
I then want to check if the value is numeric or a string. I am doing this:
isDefined('understand') and isnumeric(understand)
But it always returns "NO".
Seems like a good case for REGEX, but that's not my strength. If you are always looking for the value of the same item (construction), you could take advantage of the underlying Java and use the STRING.split() method. Then use the Coldfusion val() function to see what you get. The following solution assumes that 0 is not a valid value. If it is then you have more work to do.
<cfscript>
target=understand;
//target=understand2; //uncomment to try the second values
token="construction=";
potentialValues = target.split(token); //creates an array of values using the token as a separator
for (item in potentialValues )
{
writeoutput(val(item) & "<br />"); //val will grab the numerical value and ignore everything that follows. No number will become 0
}
</cfscript>
Try this:
constructionIsAt = find(understand, "construction");
characterAfterConstruction = mid(understand, constructionIsAt + 13, 1);
if isNumeric(characterAfterConstruction) {
code for numeric
}
else {
code for non numeric
}
I need to display an output from a data record that is formatted similar to this: XXXX:12345 (Xxxxxxxxx)
However, the only data I want to output is the "12345" and with two preceding zeros, i.e. the output should look like "0012345". The "12345" in the record is example only, each record has a unique number assigned. An example record looks like this: CAST:98765 (RPOS1234-XY)
Can I use the ReplaceNoCase() to pull only that data out of the record? If so, how would I write the code to remove the unwanted characters?
You can do this in one line of code using a few functions.
str = 'CAST:98765 (RPOS1234-XY)';
projectCode = '00' & listLast( listFirst( str, ' ' ), ':' );
writeDump( projectCode );
To explain this code from the inner most function going out.
ListFirst() gets the first element in an a list based on the delimiter you specify, in this case the delimiter is ' ' - a space - yes, you can use a space as a delimiter.
ListLast() gets the last element in a list based on the delimiter you specify, in this case the delimiter is ':'
The first part simplt appends '00' to the result of the above function calls.
If I had to use reReplaceNoCase or reFindNoCase this is how I would do it.
function parseTokenUsingReFindNoCase(token) {
var local = {};
// use regex to locate position of number (see only set of parentheses in regex pattern)
local.positions = reFindNoCase("^.+:(\d+).+$", arguments.token, 1, true);
// obtain the token substring and ensure at least 7 digits with preceding 0's
local.result = numberFormat( mid(arguments.token, local.positions.pos[2], local.positions.len[2]), repeatString(0, 7));
return local.result;
}
function parseTokenUsingReReplaceNoCase(token) {
var local = {};
// use regex to strip away text before and after the token
local.result = reReplaceNoCase(arguments.token, "(^\D+|\s.+$)", "", "all");
// ensure at least 7 digits with preceding 0's
local.result = numberFormat(local.result, repeatString(0, 7));
return local.result;
}
<h1>ParseToken</h1>
<h2>Using ReFindNoCase</h2>
<cfdump var="#parseTokenUsingReFindNoCase("CAST:98765 (RPOS1234-XY)")#" /><br>
<cfdump var="#parseTokenUsingReFindNoCase("CAST:591498 (FUBAR56-XE)")#" /><br>
<cfdump var="#parseTokenUsingReFindNoCase("CAST:784 (RFP4542-LL)")#" /><br>
<h2>Using ReReplaceNoCase</h2>
<cfdump var="#parseTokenUsingReReplaceNoCase("CAST:98765 (RPOS1234-XY)")#" /><br>
<cfdump var="#parseTokenUsingReReplaceNoCase("CAST:591498 (FUBAR56-XE)")#" /><br>
<cfdump var="#parseTokenUsingReReplaceNoCase("CAST:784 (RFP4542-LL)")#" /><br>
ParseToken
Using ReFindNoCase
0098765
0591498
0000784
Using ReReplaceNoCase
0098765
0591498
0000784
It doesn't use replaceNoCase, but based on your comments this will work:
<cfset castTicket = projectCode>
<!--- strip the first 5 characters, since it is always "CAST " --->
<cfset castTicket = removechars(castTicket, 1,5)>
<!--- now return the leftmost characters, up to the space --->
<cfset castTicket = left(castTicket, find(" ", castTicket) )>
<!--- format the number so it has 7 digits (2 leading zeros in this case) --->
<cfset castTicket = NumberFormat(castTicket, 0000000)>
<cfoutput>#castTicket#</cfoutput>
Returns:
0012345
I am using ColdFusion 9.0.1.
I am trying to test whether a user has provided a non alphanumeric value. If they have, I want to return false. I'm pretty sure I am close, but I keep getting an error:
Complex object types cannot be converted to simple values.
I've tried multiple ways of making this work, but I can't get it to work.
Specifically, I want to allow only a through z and 0 through 9. No spaces, or special characters.
Can you help me tweak this?
<cfscript>
LOCAL.Description = trim(left(ARGUMENTS.Description, 15));
if (len(LOCAL.Description) lte 4) {
return false;
} else if (reMatchNoCase("[^A-Za-z0-9_]", LOCAL.Description) neq "") {
return false;
} else {
return true;
</cfscript>
W
reMatchNoCase returns Array which cannot be compared to the string, use ArrayLen() on the result in order to find out if there any matches
There is actually another problem in your code. First line will produce an error if the length of the description is less than 15, which means that the first IF is obsolete since it will always be false.
reMatchNoCase("[^A-Za-z0-9_]", LOCAL.Description) neq ""
It is because ReMatchNoCase returns an array, not a simple string. Either check the array length, or better yet, use ReFindNoCase instead. It returns the position of the first match, or 0 if it was not found.
You can also try the following approach:
<cfscript>
local.description = trim(local.description);
return reFind("(?i)^[A-Z0-9_]{5,}$", local.description)?true:false;
</cfscript>
I'm late to the party but reFindNoCase is the optimal solution in 2021. Here's how I would handle the code in the original question:
// best practice not to have a local var name identical to an argument var
var myValue = trim( left( arguments.description, 15 ) );
// return false if myValue is less than 4 or has special characters
return(
!len( myValue ) lte 4 &&
!reFindNoCase( "[^a-z0-9]", myValue )
);