7

I recently ran into a piece of code very much like this one:

var nHours = parseInt(txtHours);
if( isNaN(nHours))  // Do something
else // Do something else with the value

The developer who wrote this code was under the impression that nHours would either be an integer that exactly matched txtHours or NaN. There are several things wrong with this assumption.

First, the developer left of the radix argument which means input of "09" would result in a value of 0 instead of 9. This issue can be resolved by adding the radix in like so:

var nHours = parseInt(txtHours,10);
if( isNaN(nHours))  // Do something
else // Do something else with the value

Next, input of "1.5" will result in a value of 1 instead of NaN which is not what the developer expected since 1.5 is not an integer. Likewise a value of "1a" will result in a value of 1 instead of NaN.

All of these issues are somewhat understandable since this is one of the most common examples of how to convert a string to an integer and most places don't discuss these cases.

At any rate it got me thinking that I'm not aware of any built in way to get an integer like this. There is Number(txtHours) (or +txtHours) which comes closer but accepts non-integer numbers and will treat null and "" as 0 instead of NaN.

To help the developer out I provided the following function:

function ConvertToInteger(text)
{
    var number = Math.floor(+text);
    return text && number == text ? number : NaN;
}

This seems to cover all the above issues. Does anyone know of anything wrong with this technique or maybe a simpler way to get the same results?

drs9222
  • 4,448
  • 3
  • 33
  • 41
  • Possibly duplicated [http://stackoverflow.com/questions/131406/what-is-the-best-method-to-convert-to-an-integer-in-javascript](http://stackoverflow.com/questions/131406/what-is-the-best-method-to-convert-to-an-integer-in-javascript) – subosito Nov 12 '10 at 19:39
  • `Number(txtHours)` instead of `number(txtHours)` – Šime Vidas Nov 12 '10 at 19:44
  • 1
    Also, there is a convention that constructor function names are capitalized, and regular function names not. I recommend sticking to that convention by using a lowercase name like `convertToInteger` or `toInteger`. Other than that, the function looks pretty solid to me. – Šime Vidas Nov 12 '10 at 19:52
  • 1
    `ConvertToInteger(true)` returns `1` which is not a desired behavior, I assume. – Šime Vidas Nov 12 '10 at 19:56
  • Seems like having some input validation where these values come into the system may be a worthwhile approach. – Sean Reifschneider Nov 13 '10 at 04:20
  • `Math.floor(+x)` handles corner cases the same as `(x | 0)`, but round towards zero semantics seems more intuitive (to someone familiar with C/java float->int casts) than round towards floor. – Mike Samuel Nov 13 '10 at 18:55
  • See the top answer for this related question: https://stackoverflow.com/a/3257124/3950982 Use `var num = Number(value); if (isNaN(value)) { ... }` and then you can presumably test if `Math.floor(num) === num` (although I don't know if there are floating point equality issues with that last step). – Luke Hutchison Jul 24 '22 at 11:31

4 Answers4

4

Here, that's what I came up with:

function integer(x) {
    if (typeof x !== "number" && typeof x !== "string" || x === "") {
        return NaN;
    } else {
        x = Number(x);
        return x === Math.floor(x) ? x : NaN;
    }
}

(Note: I updated this function to saveguard against white-space strings. See below.)

The idea is to only accept arguments which type is either Number or String (but not the empty string value). Then a conversion to Number is done (in case it was a string), and finally its value is compared to the floor() value to determine if the number is a integer or not.

integer(); // NaN
integer(""); // NaN
integer(null); // NaN
integer(true); // NaN
integer(false); // NaN
integer("1a"); // NaN
integer("1.3"); // NaN
integer(1.3); // NaN    
integer(7); // 7

However, the NaN value is "misused" here, since floats and strings representing floats result in NaN, and that is technically not true.

Also, note that because of the way strings are converted into numbers, the string argument may have trailing or leading white-space, or leading zeroes:

integer("   3   "); // 3    
integer("0003"); // 3

Another approach...

You can use a regular expression if the input value is a string. This regexp: /^\s*(\+|-)?\d+\s*$/ will match strings that represent integers.

UPDATED FUNCTION!

function integer(x) {
    if ( typeof x === "string" && /^\s*(\+|-)?\d+\s*$/.test(x) ) {
        x = Number(x);
    }
    if ( typeof x === "number" ) {
        return x === Math.floor(x) ? x : NaN;
    }
    return NaN;
}

This version of integer() is more strict as it allows only strings that follow a certain pattern (which is tested with a regexp). It produces the same results as the other integer() function, except that it additionally disregards all white-space strings (as pointed out by @CMS).

Updated again!

I noticed @Zecc's answer and simplified the code a bit... I guess this works, too:

function integer(x) {
    if( /^\s*(\+|-)?\d+\s*$/.test(String(x)) ){
        return parseInt(x, 10);
    }
    return Number.NaN;
}  

It probaly isn't the fastest solution (in terms of performance), but I like its simplicity :)

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • @CMS Oh, what a mess :) A regexp check could solve this problem. – Šime Vidas Nov 12 '10 at 21:18
  • I'm aware that using NaN is misuse but it seemed as good a way as any to represent a non integer. After all there is no NaI. There are several things I could have done including using null, undefined, an object, or multiple functions but NaN was what the original code was looking for and it seemed clear enough to go forward with it. – drs9222 Nov 14 '10 at 15:32
  • My first thought when I saw this was to use a regular expression but it seemed like overkill for something I thought should be very simple. If we wanted to we could avoid it here by trimming the input if it is a string. – drs9222 Nov 14 '10 at 15:41
  • @drs9222 Yea, the NaN value is good enough for this scenario. Especially since there is a isNaN() function, so you can test the return value easily to determine if the input is a valid integer or not. `x = integer(input); if ( !isNaN(x) ) { /* process the input */ } else { /* alert user that input is invalid */ }` – Šime Vidas Nov 14 '10 at 15:47
  • @drs9222 Yes, trimming the string is a great idea. The trim() method would do the job, but it is not implemented in IE8 and below, so that a custom trim() method should then be defined for those browsers (which is an easy task, btw). However, regular expressions are very strict which makes the whole thing more robust. – Šime Vidas Nov 14 '10 at 15:55
  • I just remembered something: `typeof new Number(8) === "object"` and `typeof new String("123") === "object"`, for some reason. So those type checks may create some trouble. – Zecc Nov 15 '10 at 16:33
  • On the other hand, `fakeNumber = { toString: function(){return "7";} }` will gladly comply to that last version of `integer`. But I don't think that's a problem. :) – Zecc Nov 15 '10 at 16:35
  • @Zecc String(x) and Number(x) convert to the primitive type. new String(x) and new Number(x) return objects. That's how it's defined in the spec. – Šime Vidas Nov 15 '10 at 17:00
1

Here's my attempt:

function integer(x) {
    var n = parseFloat(x); // No need to check typeof x; parseFloat does it for us
    if(!isNaN(n) && /^\s*(\+|-)?\d+\s*$/.test(String(x))){
        return n;
    }
    return Number.NaN;
}

I have to credit Šime Vidas for the regex, though I would get there myself.

Edit: I wasn't aware there was a NaN global. I've always used Number.NaN.
Live and learn.

Zecc
  • 4,220
  • 19
  • 17
  • 1
    Actually, using Number.NaN instead of NaN is a wise choice, since the global variable NaN can be overwritten with any other value ( var NaN = "foo" ), and the Number.NaN property cannot. – Šime Vidas Nov 14 '10 at 15:58
1

My Solution involves some cheap trick. It based on the fact that bit operators in Javascript convert their operands to integers.

I wasn't quite sure if strings representing integers should work so here are two different solutions.

function integer (number) { 
  return ~~number == number ? ~~number : NaN; 
}

function integer (number) {
  return ~~number === number ? ~~number : NaN;
}

The first one will work with both integers as strings, the second one won't. The bitwise not (~) operator will convert its operand to an integer. This method fails for integers bigger which can't be represented by the 32bit wide representation of integers (-2147483647 .. 2147483647).

evilpie
  • 2,718
  • 20
  • 21
-1

You can first convert a String to an Integer, and then back to a String again. Then check if first and second strings match.

Edit: an example of what I meant:

function cs (stringInt) {
    var trimmed = stringInt.trim();     // trim original string
    var num = parseInt(trimmed, 10);    // convert string to integer
    newString = num + "";               // convert newly created integer back to string
    console.log(newString);             // (works in at least Firefox and Chrome) check what's new string like
    return (newString == trimmed);      // if they are identical, you can be sure that original string is an integer
}

This function will return true if a string you put in is really an integer. It can be modified if you don't want trimming. Using leading zeroes will fail, but, once again, you can get rid of them in this function if you want. This way, you don't need to mess around with NaN or regex, you can easily check validity of your stringified integer.

darioo
  • 46,442
  • 10
  • 75
  • 103
  • @darioo Convert a string to an integer... how? There are various methods. – Šime Vidas Nov 13 '10 at 15:39
  • @Šime Vidas: I've updated my answer. I'd say parseInt is still the best way for converting a string to an integer. – darioo Nov 14 '10 at 13:26
  • I suspect you did this by design but your example will fail if anything other than a string, including an integer, is used as input. It also does not return the actual integer for use. – drs9222 Nov 14 '10 at 14:43
  • @drs9222: well, you did ask for something that will convert a string, not anything else. You don't have to use this function exactly as written, you can modify it yourself to suit your needs. But, you can use it for checking if an inputted string really is an integer, and then convert it using parseInt for actual use. What I've written is more of an utility function. – darioo Nov 14 '10 at 14:49
  • I understand that and I just reread my original question and you are right I did say string. However, I also specifically mentioned null. I guess I was thrown off by the tests I had run against my example and the other answers which would accept anything as input. – drs9222 Nov 14 '10 at 15:23
  • @drs9222: well, it would be trivial to add `""` and `null` as special cases in this functions. The thing is, you want some special behavior like `""` and `null` being converted to 0. From my understanding of Javascript, there are no general purpose functions that will do exactly what you want, so you have to roll out something on your own. If you want answers without ambiguities, you should specify your question a bit more precisely. – darioo Nov 14 '10 at 15:29