2

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?

espresso_coffee
  • 5,980
  • 11
  • 83
  • 193

3 Answers3

6

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.

Alex
  • 7,743
  • 1
  • 18
  • 38
  • If you use this in real life, note the change to my answer regarding commas. – Dan Bracuk Sep 11 '17 at 12:05
  • Testing for isSimpleValue is a good addition and will prevent array/struct objects from throwing an error. I don't often pass these types of values to the function, but you never know if/when a parameter could be overwritten and cause problems. (I'm updating my personal UDF to include this.) Thanks. – James Moberg Sep 11 '17 at 14:10
3

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

James Moberg
  • 4,360
  • 1
  • 22
  • 21
  • Why `ucase` and `YesNoFormat`? Change `refind("^-?\d+$", num)` to `refind("^-?\d+$", num) gt 0` and it stays boolean. – Alex Sep 09 '17 at 13:14
  • `GTE -2147483647` doesn't seem correct. `-2147483648` still fits into the signed integer range of 32 Bits. – Alex Sep 09 '17 at 13:29
  • Sorry. I had taken this from a visual output test I was used. Native booleans values were dumped in uppercase and YesNoFormat values were a mixed-cased text string. I had used UCase to enforce the same visual style. (I think this was when using CF9.) – James Moberg Sep 09 '17 at 15:36
  • If you use this in real life, note the change to my answer regarding commas. – Dan Bracuk Sep 11 '17 at 12:05
  • @DanBracuk This UDF evaluates the value without any modification or translation attempt. The strings "07,20,1969" and "1,234" are not a valid integers according to CFParam or CFQueryParam... so testing a modified value & then using the original value will result in an error. On my form-based inputs, I intentionally use javascript to validate & limit min/max values (and prevent commas from being typed or pasted into the field.) – James Moberg Sep 11 '17 at 13:24
  • I still thought it worth mentioning for the benefit of people who read this thread in the future. – Dan Bracuk Sep 11 '17 at 14:51
1

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.

Dan Bracuk
  • 20,699
  • 4
  • 26
  • 43
  • Way we use round? Is that better than isValid() ? Thanks for your help. – espresso_coffee Sep 08 '17 at 21:46
  • @espresso_coffee `isNumeric()` accepts floating-point numbers. He uses `round()` to cut off the precision and then compares the value to itself to make sure the number is a whole number (integer). – Alex Sep 09 '17 at 13:38
  • As a side note: This function doesn't check the integer range. This could result in errors if you validate inputs to put them into an `int` field in a database, which usually declare `int` with a size of 4 Byte. – Alex Sep 09 '17 at 13:49
  • I have function that checks each value and checks the range. In that case solution above should be the best for my function. Thank you! – espresso_coffee Sep 10 '17 at 20:42
  • @espresso_coffee According to my test script https://trycf.com/gist/fab1104a3a9074434ff336630dd5ffd1, this method fails that following values: 192292121121121212.1 2147483648 "#CHR(28)#00501#CHR(28)#" " 123 " " 123" "123 " "1,234" and "07,20,1969". Built-in CFQueryParam & CFParam functions also disagree with this answer and will throw CFErrors. Also, don't forget that VAL("1,234") will only return "1". – James Moberg Sep 11 '17 at 13:32
  • @JamesMoberg What is you suggestion? Which solution will be better? – espresso_coffee Sep 11 '17 at 13:34