coldfusion IIF statement with parameters - coldfusion

I'm working on a coldfusion line trying to work out what this line means. I'm new to CF so excuse my
ignorance.
<CFSET is_box = IIF(_boxes[1].name EQ application.box,1,0)>
I presume _boxes is an array of objects and if the index 1 in that array is equal to application.box then what does 1, 0 mean.
Is it like a shorthand js statement where is does the below.
is_box = ( _boxes[1].name == application.box ) ? 1 : 0;

Is it like a shorthand js statement where is does the below.
yes it is, except the string comparison is case-insensitive.
http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec22c24-7f4f.html
edit: You may, if you're using CF9 or above, refactor it into
<CFSET is_box = _boxes[1].name EQ application.box ? 1 : 0>

Related

Sum up dynamic fields in list

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>

Using CGI Scope to Create a working URL [duplicate]

I have a url.LoginID, and I'd like to remove it from the address bar when the user clicks on the link to login. It has to be a bookmark, it can't be a form submit.
Q: How do I remove ?LoginID from Index.cfm?LoginID=XYZ&AssignmentID=123
It's probably something along the lines of:
<cflocation url="#cgi.SCRIPT_NAME#?#cgi.QUERY_STRING#" addtoken="no">
Looks like you are on the right track.
If loginID is the only thing in the query string, you can simply cflocation to the destination page without the query string.
If there is other data in the query string, then you can do something like this:
<cfset q = reReplaceNoCase(cgi.query_string, "LOGINID=[^&]+&?", "")>
<cflocation url="#cgi.SCRIPT_NAME#?#q#">
This essentially removes loginid and everything until either the en of the string or the next URL variable.
As usual, there's already a UDF that someone has written available on CFLIB: queryStringDeleteVar
You can then do like so
<cflocation
url="#cgi.SCRIPT_NAME#?#queryStringDeleteVar("LoginID",cgi.QUERY_STRING)#"
addtoken="no"
>
CGI.QUERY_STRING is actually the default for the second arg, so this will work just as well
<cflocation
url="#cgi.SCRIPT_NAME#?#queryStringDeleteVar("LoginID")#"
addtoken="no"
>
Here's the code for queryStringDeleteVar:
<cfscript>
/**
* Deletes a var from a query string.
* Idea for multiple args from Michael Stephenson (michael.stephenson#adtran.com)
*
* #param variable A variable, or a list of variables, to delete from the query string.
* #param qs Query string to modify. Defaults to CGI.QUERY_STRING.
* #return Returns a string.
* #author Nathan Dintenfass (michael.stephenson#adtran.comnathan#changemedia.com)
* #version 1.1, February 24, 2002
*/
function queryStringDeleteVar(variable){
//var to hold the final string
var string = "";
//vars for use in the loop, so we don't have to evaluate lists and arrays more than once
var ii = 1;
var thisVar = "";
var thisIndex = "";
var array = "";
//if there is a second argument, use that as the query string, otherwise default to cgi.query_string
var qs = cgi.query_string;
if(arrayLen(arguments) GT 1)
qs = arguments[2];
//put the query string into an array for easier looping
array = listToArray(qs,"&");
//now, loop over the array and rebuild the string
for(ii = 1; ii lte arrayLen(array); ii = ii + 1){
thisIndex = array[ii];
thisVar = listFirst(thisIndex,"=");
//if this is the var, edit it to the value, otherwise, just append
if(not listFind(variable,thisVar))
string = listAppend(string,thisIndex,"&");
}
//return the string
return string;
}
</cfscript>
There are number of ways to do this, here is one way using a list loop to read through your existing parameters and check for the one you want to ignore:
<cfset newParams = "" />
<cfloop list="#cgi.query_string#" delimiters="&" index="i">
<cfif listFirst(i, "=") neq "loginID">
<cfset newParams = listAppend(newParams, i, "&") />
</cfif>
</cfloop>
<cflocation url="#cgi.script_name#?#newParams#" addtoken="no">
Hope that helps!
Suppose you don't really want to remove the ? to keep the URL valid, so simple regex should work:
QUERY_STRING = ReReplaceNoCase(cgi.QUERY_STRING, "LoginID=.+\&", "");
BTW, I'm not sure why do you keep LoginID in URL at all, it may be insecure approach. Using sessions sounds like a better idea.
Edit: Ben's regex is better, because my version is so simple that will "eat" all key=value pairs before last one.
Insert famous Zawinski two problem regex quote and solve differently:
<cfset copy = duplicate(url)>
<cfset structDelete(copy, "loginid")>
<cfset entries = []>
<cfloop collection="#copy#" item="key">
<cfset arrayAppend(entries, "#key#=#copy[key]#")>
</cfloop>
<cfoutput>#arrayToList(entries, "&")#</cfoutput>

trying to find the value is numeric or integer from string

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
}

How to do a single line If statement in VBScript for Classic-ASP?

The "single line if statement" exists in C# and VB.NET as in many other programming and script languages in the following format
lunchLocation = (dayOfTheWeek == "Tuesday") ? "Fuddruckers" : "Food Court";
does anyone know if there is even in VBScript and what's the extact syntax?
The conditional ternary operator doesn't exist out of the box, but it's pretty easy to create your own version in VBScript:
Function IIf(bClause, sTrue, sFalse)
If CBool(bClause) Then
IIf = sTrue
Else
IIf = sFalse
End If
End Function
You can then use this, as per your example:
lunchLocation = IIf(dayOfTheWeek = "Tuesday", "Fuddruckers", "Food Court")
The advantage of this over using a single line If/Then/Else is that it can be directly concatenated with other strings. Using If/Then/Else on a single line must be the only statement on that line.
There is no error checking on this, and the function expects a well formed expression that can be evaluated to a boolean passed in as the clause. For a more complicated and comprehensive answer see below. Hopefully this simple response neatly demonstrates the logic behind the answer though.
It's also worth noting that unlike a real ternary operator, both the sTrue and sFalse parameters will be evaluated regardless of the value of bClause. This is fine if you use it with strings as in the question, but be very careful if you pass in functions as the second and third parameters!
VBScript does not have any ternary operator.
A close solution in a single line and without using a user defined function, pure VBScript:
If dayOfTheWeek = "Tuesday" Then lunchLocation = "Fuddruckers" Else lunchLocation = "Food Court"
BTW, you can use JScript in Classic ASP if ternary opertor is so important to you.
edited 2017/01/28 to adapt to some of the non boolean condition arguments
Note: If all you need is to select an string based on an boolean value, please, use the code in the Polinominal's answer. It is simpler and faster than the code in this answer.
For a simple but more "flexible" solution, this code (the original code in this answer) should handle the usual basic scenarios
Function IIf( Expression, TruePart, FalsePart)
Dim bExpression
bExpression = False
On Error Resume Next
bExpression = CBool( Expression )
On Error Goto 0
If bExpression Then
If IsObject(TruePart) Then
Set IIf = TruePart
Else
IIf = TruePart
End If
Else
If IsObject(FalsePart) Then
Set IIf = FalsePart
Else
IIf = FalsePart
End If
End If
End Function
If uses the Cbool function to try to convert the passed Expression argument to a boolean, and accepts any type of value in the TrueValue and FalseValue arguments. For general usage this is fast, safe and fully complies to documented VBScript behaviour.
The only "problem" with this code is that the behaviour of the CBool is not fully "intuitive" for some data types, at least for those of us that constantly change between vbscript and javascript. While numeric values are coherent (a 0 is a False and any other numeric value is a True), non numeric types generate a runtime error (in previous code handled as false), except if it is a string with numeric content or that can be interpreted as true or false value in english or in the OS locale.
If you need it, a VBScript version "equivalent" to the ? javascript ternary operator is
Function IIf( Expression, TruePart, FalsePart )
Dim vType, bExpression
vType = VarType( Expression )
Select Case vType
Case vbBoolean : bExpression = Expression
Case vbString : bExpression = Len( Expression ) > 0
Case vbEmpty, vbNull, vbError : bExpression = False
Case vbObject : bExpression = Not (Expression Is Nothing)
Case vbDate, vbDataObject : bExpression = True
Case Else
If vType > 8192 Then
bExpression = True
Else
bExpression = False
On Error Resume Next
bExpression = CBool( Expression )
On Error Goto 0
End If
End Select
If bExpression Then
If IsObject( TruePart ) Then
Set IIf = TruePart
Else
IIf = TruePart
End If
Else
If IsObject( FalsePart ) Then
Set IIf = FalsePart
Else
IIf = FalsePart
End If
End If
End Function
BUT independently of the version used, be careful, you are calling a function, not using a ternary operator. Any code, or function call you put in TruePart of FalsePart WILL BE EXECUTED independently of the value of the condition. So this code
value = IIf( 2 > 3 , DoSomething(), DontDoSomething() )
WILL EXECUTE the two functions. Only the correct value will be returned to value var.
There's a weird trick possible (hi, Python!) for exact one-liner:
lunchLocation = array("Food Court", "Fuddruckers")(-(dayOfTheWeek = "Tuesday"))
The "magic" works because of a boolean operation specifics in VBScript.
True is actually -1 and False is 0, therefore we can use it as an index for array (just get rid of a minus). Then the first item of array will be a value for False condition and second item for True.
related to #MC_ND answer:
to execute only one function, you can do something like that:
If VarType(TruePart) = vbString and InStr(1,TruePart,"function:") = 1 then
IIf = GetRef(Mid(TruePart,10))()
Else
IIf = TruePart
End If
the same for the FalsePart, and call IIf() it like that:
value = IIf( 2 > 3 , "function:DoSomething", "function:DontDoSomething" )
and will call DoSomething() or DontDoSomething()

How to disallow non-alphanumeric characters in ColdFusion using a RegEx

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 )
);