Best way to check if value is integer ? Coldfusion 9 - coldfusion

I have fields to test and make sure they only accept integers. There is few functions but I wasn't sure which one is the best. First I tried isValid("integer",value) but I have discovered that "1,5" will be accepted as an integer. So then I tried isNumeric(value) but this will accept values like 1.5. I'm wondering what should be the best way to check for integers? Maybe two combine these two functions like:
<cfif isValid("integer",value) AND isNumeric(value)>
Or there is better way to do this?

cfscript
// Returns if the provided value is a signed integer up to 32 Bit.
function isINT(any value) {
return (
isSimpleValue(ARGUMENTS.value) &&
(reFind("^\-?[0-9]{1,10}$", ARGUMENTS.value) > 0) &&
(ARGUMENTS.value <= 2147483647) &&
(ARGUMENTS.value >= -2147483648)
);
}
cftag
<cffunction name="isINT" access="public" output="false" returnType="boolean"
hint="Returns if the provided value is a signed integer up to 32 Bit.">
<cfargument name="value" type="any" required="true">
<cfreturn (
isSimpleValue(ARGUMENTS.value) and
(reFind("^\-?[0-9]{1,10}$", ARGUMENTS.value) gt 0) and
(ARGUMENTS.value lte 2147483647) and
(ARGUMENTS.value gte -2147483648)
)>
</cffunction>
isSimpleValue making sure the input is a primitive type (by CF means), because all numbers are considered simple values in CF (string conversion)
reFind regular expression checking digits-only (with or without sign), minimum of one digit, maximum of ten digits (implicit call of toString here)
check the range, all numeric types fit into 4 Bytes, thus no need to "upgrade" the type (as you would need to with BigInteger, BigDecimal etc.)
If you don't need the range check for 4 Byte integers, #DanBracuk posted an answer with a function that performs around 5-6 times faster than this one.

Here's the isInteger UDF that I prefer using:
function isInteger(num){
return YesNoFormat(refind("^-?\d+$", num) AND VAL(num) LTE 2147483647 AND VAL(num) GTE -2147483648);
}
Here are some tests to determine how it functions and compares against the various built-in functions.
https://gist.github.com/JamoCA/fab1104a3a9074434ff336630dd5ffd1
View the results using TryCF.com
https://trycf.com/gist/fab1104a3a9074434ff336630dd5ffd1

You could try this:
value = replace(value, ',', '', 'all');
numberIsInteger = isNumeric(value) && round(value) == value ? true : false;
Note
People often include commas in large numbers such as 1,000,000. isNumeric will return false for that string, as will the refind function in the other answers.

Related

How to add padding before decoding a base64 string?

ColdFusion's binaryDecode(input, 'base64') is picky because padding is mandatory.
What is the correct way to add padding with = to a base64 value?
1.) Ben Nadel uses:
value &= repeatString( "=", ( 4 - ( len( value ) % 4 ) ) );
2.) Arlo Carreon uses
<cfset res = Len(raw_str) % 4>
<cfif res eq 2>
<cfset raw_str &= "==">
<cfelseif res eq 3>
<cfset raw_str &= "=">
</cfif>
While they both seem to work, the 1st solution may return 1 to 4 ='s, while the 2nd solution may return 0, 1 or 2 ='s. Wikipedia on Base64 Padding seems to indicate that a valid base64 value should really have only 1 or 2 ='s.
1st solution seems to work with all base64 value lengths, but it may sometimes return 3 or 4 ='s which is kind of strange. Where as the 2nd solution may fail for base64 value that has remainder of 1. CF throws The input and output encodings are not same.
Padding is mandatory by specification (RFC 2045, 3548/4648).
Implementations MUST include appropriate pad characters at the end of encoded data unless the specification referring to this document explicitly states otherwise.
The correct way to fix missing padding is to append = until ( len(value) % 4 ) eq 0. This means a correctly padded Base64 string can only end:
without =
with =
with ==
The specification allows ("may") ignoring excessive padding.
If more than the allowed number of pad characters is found at the end of the string (e.g., a base 64 string terminated with "==="), the excess pad characters MAY also be ignored.
Can you elaborate what you mean by The input and output encodings are not same.? This sounds like an invalid Base64 encoded string. You may want to check what toBinary() returns for the input. It will probably tell you The parameter 1 of function ToBinary, which is now ... must be a base-64 encoded string, which is exactly the problem.

How to get sum of array for specific position?

I have an array:
<cfset fullarray = listtoArray (listofcollumnvalues)>
And it had for example:
fullarray[1]=20
fullarray[2]=11
fullarray[3]=4
fullarray[4]=12.2
etc.
And I wanted to add the sum of the the X position below number:
for example if I wanted the sum of the second element and below
2 + 3 + 4= 27.2
ColdFusion 10 introduced the ArraySlice function. It returns an array when you give it an array, a starting location, and an optional length. So ArraySlice(myArray,3,4) would return a "sub-array" with the elements that start at position 3 and includes 4 elements.
Based on your example:
mySum = ArraySum(ArraySlice(fullarray,2))
If you're on CF 9 or below, you can use a UDF. At CFLib.org there is
arraySlice
arraySlice2
Warning! arraySlice uses "start" and "end" element arguments, while arraySlice2 uses "start" and "length" arguments, like the built-in CF10 function.
Here's something that will work on older versions.
<cfscript>
function listSum(listStr)
{
var delim = ",";
if(ArrayLen(Arguments) GTE 2)
delim = Arguments[2];
return ArraySum(ListToArray(listStr, delim));
}
</cfscript>
Simply envoke it as #listSum(MyListOrArray)#
This is a formatted comment. You define your array as this:
<cfset fullarray = listtoArray (listofcollumnvalues)>
The variable, listofcollumnvalues looks like it might represent results of a query object. If that's the case, You might be doing more work than necessary, because each column of a query is effectively an array. Consider for example this query:
<cfquery name = "queryName">
select field1, numericalField
from etc
</cfquery>
You can actually do stuff like this:
numericalFieldSum = ArraySum(queryName['numericalField'];
It's not exactly what you want, but it illustrates that you might not need your initial command.
Continuing with the assumption that you started with a query, a query of queries might give you what you want.
<cfquery name = "mySum" dbtype = "query">
select sum(numericalField) theSumIWant
from queryName
where your conditions are met
</cfquery>
Of course, if listofcollumnvalues does not come from a query, you should declare this answer to be useless twaddle and downvote accordingly.

ColdFusion 9: int and type="numeric" nasty bug?

I've just experienced a behaviour that defies any logic and could potentially lead to serious issues and was wondering if it was a bug or if the behaviour was itended and what are the best practices to circumvent the issue? If it's a bug, is there a patch?
Here's the two wierd behaviours that when put together are a threat to any system's data integrity.
int('1 2') -> 41276
isValid('numeric', '1 2') -> true
Why? Well let's see...
<cffunction name="deleteSomething" access="public" returntype="void">
<cfargument name="somethingId" type="numeric" required="yes">
<cfquery datasource="#dsn()#">
DELETE
FROM Something
WHERE id = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.somethingId#">;
</cfquery>
</cffunction>
<cfset deleteSomething('1 2')>
Here, the type="numeric" arguments validation (which perhaps is based on the same algorithm as isValid?) doesn't throw with '1 2'. Even worse, cfqueryparam cfsqltype="cf_sql_integer" seems to be using int to convert the value which will end up being 41276.
In other words, deleteSomething('1 2') will delete the entity with id 41276 instead of throwing an exception since the value 1 2 is obviously not numeric.
Now, the only fix I thought of is to perform additionnal argument validation using isValid('integer', ... or a regular expression, but that's a real pain and besides, I never understood why they haven't implemented type="integer"?
Obviously, I also always made the false assumption that cfqueryparam type="cf_sql_integer" would validate that the value passed is a valid integer.
EDIT:
It seems that even isvalid('integer', ... is also not reliable as we can see in
Why isvalid("integer","1,5") = YES?
EDIT2:
I know that I could add additionnal arguments validation for every expected integer argument in every function, however that would require to fix a huge code base in my case and it's also very error-prone. It also makes the built-in argument validation completely useless in this case.
I would rather prefer a solution where I could create and apply an unofficial patch. Is that a realistic option? If so I would like to be pointed out in the right direction.
EDIT3: It doesn't solves all the problems, but CF11 added support for a strictNumberValidation application level configuration.
"Starting from ColdFusion 11, this function evaluates on a more strict
basis. Setting this value to false makes the isValid function to
behave in the older way. This setting effects cfargument, cfparam and
cfform tags wherever integer & numeric validation is used. Based on
this setting, the validation reflects in those tags as well."
This is a variation on that theme from the other question. See this code (or run it on cflive.net):
<cfscript>
s = "1 2";
i = int(s);
v = isValid("numeric", s);
d = createOdbcDate(s);
writeDump([s,i,v,d]);
</cfscript>
s converts to 41276 when calling int(), and when using it as an input for createOdbcDate(), we get:
January, 02 2013 00:00:00 +0000
So "1 2" is being interpreted as "m d", with an implied year of the current year.
Which is utterly stupid. But there you go.
You can use regular expressions to find out if there are any non numeric characters in a given form field:
reFind( "[^\d-]", "1 2")
That will match any character that is not a number, not a -
If you want to check only positive numbers, you can use
reFind( "[^\d]", "1 2")
If this returns true, you do not have an integer.

Determining if a string is not null/blank and is a number and not 0?

I normally don't work in ColdFusion but there's a FTP process at work I have to create a report for with the only option right now being a ColdFusion 8 server. This FTP feed has a few issues (trash too).
So, I make the query and then I need to convert some of the string values during the output to do some math. Before that:
How do I tell if a field in the output loop: is not blank or null, is string that can be converted into a valid number, and is not 0?
Is there a simple way of doing this w/o a lot of if statements?
Thanks!
So you want to make sure that the variable is numeric but not zero?
Then you want this:
<cfif IsNumeric(MyVar) AND MyVar NEQ 0 >
Determining if a string is not null/blank and is a number and not 0?
Here's the code I would use in this case.
<cfif isDefined(stringVar) and len((trim(stringVar))) and isNumeric(stringVar)>
do stuff here
</cfif>
isDefined returns a true if the variable exists. If you know the scope of the variable, i.e., its in the form or url scope for instance, you can use structkeyExists(form,"stringVar"). I would recommend using this approach if you know the scope of the variable.
Len(trim(stringVar)) is the second check. First off it trims any leading or trailing empty spaces from the string - this makes sure that any empty variables are not passed along. Then if something is there it will return the length of the string. If its empty len will return a 0.
isNumeric(stringVar) returns a true if the variable is a number and false otherwise.
<cfif Len(field) and Val(field)>
Len() will verify the field has length (not blank--there are no NULLs in CF) and Val() will automatically convert the first character in the string into into a number--or return 0 if it cannot.
Take note of Peter's comment below; although this is the least verbose answer, Val() may fail in certain edge conditions below, ie. The field is a string but starts with a number, incorrectly converting it to a number, and evaluating to TRUE.
<cfif isNumeric(myfield) and myfield gt 0>

What can you do in ColdFusion in a single line?

Is there a way of writing this logic in a single, elegant line of code?
<cfif ThumbnailWidth EQ 0>
<cfset Width = 75>
<cfelse>
<cfset Width = ThumbnailWidth>
</cfif>
Coldfusion 9:
<!--- Syntax: ((condition) ? trueStatement : falseStatement) --->
<cfset width = ((ThumbnailWidth EQ 0) ? 75 : ThumbnailWidth) />
Coldfusion 8 and below:
<!--- Syntax: IIf(condition, trueStatement, falseStatement) --->
<cfset width = IIf((ThumbnailWidth EQ 0), 75, ThumbnailWidth) />
Some will say that IIf() is to be avoided for performance reasons. In this simple case I'm sure you'll find no difference. Ben Nadel's Blog has more discussion on IIF() performance and the new ternary operator in CF 9.
I find your original elegant enough - tells the story, easy to read. But that's definitely a personal preference. Luckily there's always at least nine ways to do anything in CFML.
You can put that on one line (CFML has no end-of-line requirements):
<cfif ThumbnailWidth EQ 0><cfset Width = 75><cfelse><cfset Width = ThumbnailWidth></cfif>
You can also use IIF() Function - it'll do the trick:
<cfset Width = IIf(ThumbnailWidth EQ 0, 75, ThumbnailWidth)>
This construct is a little odd tho' - is more clear I think. The strength of IIF() is that it can also be used inline (it is a function after all). For example:
<img src="#ImageName#" width="#IIf(ThumbnailWidth EQ 0, 75, ThumbnailWidth)#">
This last form is often used to maintain a clean(er) HTML layout while injecting dynamic code.
Like Neil said, it's fine the way it is. If you really want a single line you could make it a cfscript with a ternary operator, like:
<cfscript>width = (ThumbnailWidth == 0) ? 75 : ThumbnailWidth;</cfscript>
Haven't tested this code, but it should work.
If you are looking for concise code, then you can take it a step further than the other examples, taking advantage of CF's evaluation of non-zero values as true:
<!--- CF 9 example --->
<cfset width = ThumbnailWidth ? ThumbnailWidth : 75>
<!--- CF 8 and below --->
<cfset width = iif(ThumbnailWidth, ThumbnailWidth, 0)>
Naturally, you'll sacrifice a little clarity, but that's the tradeoff for more compact code.
I personally prefer something more along the lines of this:
<cfscript>
Width = ThumbnailWidth;
if(NOT Val(Width)) // if the Width is zero, reset it to the default width.
Width = 75;
</cfscript>