252

I would like the simplest fail-safe test to check that a string in JavaScript is a positive integer.

isNaN(str) returns true for all sorts of non-integer values and parseInt(str) is returning integers for float strings, like "2.5". And I don't want to have to use some jQuery plugin either.

Tim S. Van Haren
  • 8,861
  • 2
  • 30
  • 34
Mick Byrne
  • 14,394
  • 16
  • 76
  • 91
  • 9
    Do you allow "+2"? How about "0.1e1"? How about "1.0000"? – Phrogz May 31 '12 at 13:43
  • The `isNaN()` function behaves the way it does because the concept `Not a Number` has a very specific meaning in the IEEE 794 floating point specification. It does not intend to supply an answer to the simple colloquial question, "is this value not a number?" – Pointy May 31 '12 at 13:52
  • 3
    The question is vague to the extreme. You cannot validate that "a string is an integer", because there's no such thing - no object can be a string and a number at the same time. You probably meant "how to test if a string is a valid representation of an integer", but to answer this we need to know which language or "culture" you're talking about. For example, `٢‎٣٤` or `MCMXIX` are both valid integer representations, but I don't think you're looking for a code that would be able to parse these. Could you specify which number formats you're going to support, as people seem confused about that. – georg May 31 '12 at 15:14
  • 7
    I think 'vague to the extreme' is itself a bit extreme; but anyway... The context is validation of an quantity input field on a shopping cart used in an English speaking country, so only Western numerals. By "positive" I meant greater than zero. Would I accept the following : "+2" yes, "0.1e1" no , "1.000" sure why not. However, if you can provide an answer that can be adjusted to include / exclude these various specialised scenarios I promise to give you extra ups (and I'm sure others will too). – Mick Byrne Jun 04 '12 at 12:42

16 Answers16

357

Two answers for you:

  • Based on parsing

  • Regular expression

Note that in both cases, I've interpreted "positive integer" to include 0, even though 0 is not positive. I include notes if you want to disallow 0.

Based on Parsing

If you want it to be a normalized decimal integer string over a reasonable range of values, you can do this:

function isInDesiredForm(str) {
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

or if you want to allow whitespace and leading zeros:

function isInDesiredForm(str) {
    str = str.trim();
    if (!str) {
        return false;
    }
    str = str.replace(/^0+/, "") || "0";
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

Live testbed (without handling leading zeros or whitespace):

function isInDesiredForm(str) {
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}
function gid(id) {
    return document.getElementById(id);
}
function test(str, expect) {
    var result = isInDesiredForm(str);
    console.log(
        str + ": " +
        (result ? "Yes" : "No") +
        (expect === undefined ? "" : !!expect === !!result ? " <= OK" : " <= ERROR ***")
    );
}
gid("btn").addEventListener(
    "click",
    function() {
        test(gid("text").value);
    },
    false
);
test("1", true);
test("1.23", false);
test("1234567890123", true);
test("1234567890123.1", false);
test("0123", false); // false because we don't handle leading 0s
test(" 123 ", false); // false because we don't handle whitespace
<label>
  String:
  <input id="text" type="text" value="">
<label>
<input id="btn" type="button" value="Check">

Live testbed (with handling for leading zeros and whitespace):

function isInDesiredForm(str) {
    str = str.trim();
    if (!str) {
        return false;
    }
    str = str.replace(/^0+/, "") || "0";
    var n = Math.floor(Number(str));
    return String(n) === str && n >= 0;
}
function gid(id) {
    return document.getElementById(id);
}
function test(str, expect) {
    var result = isInDesiredForm(str);
    console.log(
        str + ": " +
        (result ? "Yes" : "No") +
        (expect === undefined ? "" : !!expect === !!result ? " <= OK" : " <= ERROR ***")
    );
}
gid("btn").addEventListener(
    "click",
    function() {
        test(gid("text").value);
    },
    false
);
test("1", true);
test("1.23", false);
test("1234567890123", true);
test("1234567890123.1", false);
test("0123", true);
test(" 123 ", true);
<label>
  String:
  <input id="text" type="text" value="">
<label>
<input id="btn" type="button" value="Check">

If you want to disallow 0, just change >= 0 to > 0. (Or, in the version that allows leading zeros, remove the || "0" on the replace line.)

How that works:

  1. In the version allowing whitespace and leading zeros:
  • str = str.trim(); removes any leading and trailing whitepace.
  • if (!str) catches a blank string and returns, no point in doing the rest of the work.
  • str = str.replace(/^0+/, "") || "0"; removes all leading 0s from the string — but if that results in a blank string, restores a single 0.
  1. Number(str): Convert str to a number; the number may well have a fractional portion, or may be NaN.

  2. Math.floor: Truncate the number (chops off any fractional portion).

  3. String(...): Converts the result back into a normal decimal string. For really big numbers, this will go to scientific notation, which may break this approach. (I don't quite know where the split is, the details are in the spec, but for whole numbers I believe it's at the point you've exceeded 21 digits [by which time the number has become very imprecise, as IEEE-754 double-precision numbers only have roughtly 15 digits of precision..)

  4. ... === str: Compares that to the original string.

  5. n >= 0: Check that it's positive.

Note that this fails for the input "+1", any input in scientific notation that doesn't turn back into the same scientific notation at the String(...) stage, and for any value that the kind of number JavaScript uses (IEEE-754 double-precision binary floating point) can't accurately represent which parses as closer to a different value than the given one (which includes many integers over 9,007,199,254,740,992; for instance, 1234567890123456789 will fail). The former is an easy fix, the latter two not so much.

Regular Expression

The other approach is to test the characters of the string via a regular expression, if your goal is to just allow (say) an optional + followed by either 0 or a string in normal decimal format:

function isInDesiredForm(str) {
    return /^\+?(0|[1-9]\d*)$/.test(str);
}

Live testbed:

function isInDesiredForm(str) {
    return /^\+?(0|[1-9]\d*)$/.test(str);
}
function gid(id) {
    return document.getElementById(id);
}
function test(str, expect) {
    var result = isInDesiredForm(str);
    console.log(
        str + ": " +
        (result ? "Yes" : "No") +
        (expect === undefined ? "" : !!expect === !!result ? " <= OK" : " <= ERROR ***")
    );
}
gid("btn").addEventListener(
    "click",
    function() {
        test(gid("text").value);
    },
    false
);
test("1", true);
test("1.23", false);
test("1234567890123", true);
test("1234567890123.1", false);
test("0123", false); // false because we don't handle leading 0s
test(" 123 ", false); // false because we don't handle whitespace
<label>
  String:
  <input id="text" type="text" value="">
<label>
<input id="btn" type="button" value="Check">

How that works:

  1. ^: Match start of string

  2. \+?: Allow a single, optional + (remove this if you don't want to)

  3. (?:...|...): Allow one of these two options (without creating a capture group):

  4. (0|...): Allow 0 on its own...

  5. (...|[1-9]\d*): ...or a number starting with something other than 0 and followed by any number of decimal digits.

  6. $: Match end of string.

If you want to disallow 0 (because it's not positive), the regular expression becomes just /^\+?[1-9]\d*$/ (e.g., we can lose the alternation that we needed to allow 0).

If you want to allow leading zeroes (0123, 00524), then just replace the alternation (?:0|[1-9]\d*) with \d+

function isInDesiredForm(str) {
    return /^\+?\d+$/.test(str);
}

If you want to allow whitespace, add \s* just after ^ and \s* just before $.

Note for when you convert that to a number: On modern engines it would probably be fine to use +str or Number(str) to do it, but older ones might extend those in a non-standard (but formerly-allowed) way that says a leading zero means octal (base 8), e.g "010" => 8. Once you've validated the number, you can safely use parseInt(str, 10) to ensure that it's parsed as decimal (base 10). parseInt would ignore garbage at the end of the string, but we've ensured there isn't any with the regex.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    both `Number(' ')` and `Number('')` return `0` while should return `NaN` instead. – neurino Mar 18 '13 at 11:34
  • @T.J.Crowder ok but I *do need* strings like ''2a'' to be rejected somehow, `parseInt` returns `2`. – neurino Mar 18 '13 at 11:41
  • @neurino: `/\D/.test('2a')` is true (because there's a non-digit). So perhaps `if (!/\D/.test(str) && !isNan((num = parseInt(str, 10))) { /* Valid number in num */}` ... Just off the top of my head... – T.J. Crowder Mar 18 '13 at 11:45
  • Performance wise, which method would be faster? Or which one is advisable? – phkavitha Sep 26 '13 at 06:37
  • @phkavitha: The answer to JavaScript performance questions is almost always "It depends" because the different engines are so different from one another. You can test your target engines/browsers using http://jsperf.com. I wouldn't think this operation would be likely to be the bottleneck in any given app... :-) – T.J. Crowder Sep 26 '13 at 07:41
  • For non-negative case you can use a one liner `String(Math.abs(~~Number(str))) !== str` – Yuri Astrakhan Feb 18 '15 at 16:25
  • regex wouldn't match something like 09 though right? which might be a problem if it's for dates or something like that – Fabio Nov 16 '15 at 19:46
  • @dietbacon: Yes, because we don't usually write leading zeroes. But I've added an option for one that allows them. – T.J. Crowder Nov 17 '15 at 07:31
  • I found that the first approach "Based on Parsing" don't seem to work with 10 or more digits, so it might be better to use `var n = Number(str).toFixed();` instead. – Camilo Dec 16 '16 at 02:20
  • @Camilo: Thanks. The problem was using `~~`, which limited it to 32-bit integers. I've fixed that, but that approach remains limited by the precision of IEEE-754 double-precision binary floating point numbers, so values like `1234567890123456789` won't work. – T.J. Crowder Dec 16 '16 at 07:37
  • To be pedantic, 0 is not a positive number, so you need n>0, not n>=0. – Slothario Dec 16 '20 at 20:55
  • @Slothario - As I said at the top of the answer: *"Note that in both cases, I've interpreted "positive integer" to include 0, even though 0 is not positive. I include notes if you want to disallow 0."* because most people act as though 0 were positive in pragmatic terms. – T.J. Crowder Dec 17 '20 at 07:08
93

Solution 1

If we consider a JavaScript integer to be a value of maximum 4294967295 (i.e. Math.pow(2,32)-1), then the following short solution will perfectly work:

function isPositiveInteger(n) {
    return n >>> 0 === parseFloat(n);
}

DESCRIPTION:

  1. Zero-fill right shift operator does three important things:
    • truncates decimal part
      • 123.45 >>> 0 === 123
    • does the shift for negative numbers
      • -1 >>> 0 === 4294967295
    • "works" in range of MAX_INT
      • 1e10 >>> 0 === 1410065408
      • 1e7 >>> 0 === 10000000
  2. parseFloat does correct parsing of string numbers (setting NaN for non numeric strings)

TESTS:

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e7"                  : false
"1e7"                   : true
"1e10"                  : false
"1edf"                  : false
" "                     : false
""                      : false

DEMO: http://jsfiddle.net/5UCy4/37/


Solution 2

Another way is good for all numeric values which are valid up to Number.MAX_VALUE, i.e. to about 1.7976931348623157e+308:

function isPositiveInteger(n) {
    return 0 === n % (!isNaN(parseFloat(n)) && 0 <= ~~n);
}

DESCRIPTION:

  1. !isNaN(parseFloat(n)) is used to filter pure string values, e.g. "", " ", "string";
  2. 0 <= ~~n filters negative and large non-integer values, e.g. "-40.1", "129000098131766699";
  3. (!isNaN(parseFloat(n)) && 0 <= ~~n) returns true if value is both numeric and positive;
  4. 0 === n % (...) checks if value is non-float -- here (...) (see 3) is evaluated as 0 in case of false, and as 1 in case of true.

TESTS:

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e10"                 : false
"1e10"                  : true
"1edf"                  : false
" "                     : false
""                      : false

DEMO: http://jsfiddle.net/5UCy4/14/


The previous version:

function isPositiveInteger(n) {
    return n == "0" || ((n | 0) > 0 && n % 1 == 0);
}

DEMO: http://jsfiddle.net/5UCy4/2/

Community
  • 1
  • 1
VisioN
  • 143,310
  • 32
  • 282
  • 281
  • Try an empty string or a large number such as 1290000981231123.1 - http://jsfiddle.net/5UCy4/1/ – Niko May 31 '12 at 14:45
  • @gdoron The same as `~~val`, cutting off fractional part. It is a bit faster since does one bitwise operation instead of two. – VisioN May 31 '12 at 21:32
  • Great answer. Personally I would favor being a bit more verbose for readability `(!isNaN(parseFloat(n)) && n % 1 === 0 && 0 <= ~~n)` – Ramy Nasr Aug 02 '17 at 14:25
  • `true, false, 0xFF`, and other values make it through `isPositiveInteger` without being detected. – Xeoncross Feb 01 '18 at 17:49
  • @Xeoncross which makes sense, as all your examples are positive integers. Under the hood, boolean values are 1 bit numbers, whereas `0xFF` is just a hexademical representation of an integer. In the answer I validate values but not variable types. – VisioN Feb 02 '18 at 08:09
  • 1
    I don't spend much time in the JavaScript world so +1 for the zero-fill right shift operator solution. It's fantastic and simple. My one nitpick is that it still returns true for whole numbers with decimals without doing an additional check. So if I wanted to use it to validate user input, `1.` and `1.00000` would return true. – David DeMar Apr 12 '18 at 15:24
  • Technically these check for a non-negative number, not a positive one. So 0 would return true, even though it isn't a positive number. – David DeMar Apr 12 '18 at 18:46
  • 1
    This one even handles padded zeros (good!). You could add that to your tests. – Rashack Sep 25 '19 at 12:47
28

The modern solution that works in node and across over 90% of all browsers (except IE and Opera Mini) is to use Number.isInteger followed by a simple positive check.

Number.isInteger(x) && x > 0

This was finalized in ECMAScript 2015.

function isPositiveInteger(x) {
    return Number.isInteger(x) && x > 0
}

The Polyfil is:

Number.isInteger = Number.isInteger || function(value) {
  return typeof value === 'number' && 
    isFinite(value) && 
    Math.floor(value) === value;
};

If you need to support input that might be in string or number form then you can use this function I wrote a large test suite against after all the existing answers (2/1/2018) failed on some form of input.

function isPositiveInteger(v) {
  var i;
  return v && (i = parseInt(v)) && i > 0 && (i === v || ''+i === v);
}
Xeoncross
  • 55,620
  • 80
  • 262
  • 364
26

Looks like a regular expression is the way to go:

var isInt = /^\+?\d+$/.test('the string');
Niko
  • 26,516
  • 9
  • 93
  • 110
  • Close, unless "1.0" or ".1e1" are allowed. – Phrogz May 31 '12 at 13:44
  • Well 1290000192379182379123782900192981231 is an integer but it's not representable exactly in JavaScript native numbers, so with a regex it's still necessary to do a numeric conversion and verify that it worked. – Pointy May 31 '12 at 13:50
  • This is a very imprecise solution. – usr May 31 '12 at 14:25
  • 1
    @usr: I suppose it allows leading `0` where you wouldn't normally expect to, but for the most part, seems fine. – T.J. Crowder May 31 '12 at 14:34
  • It doesn't check the range to be at most 2^31-1 and it doesn't allow arabic digits. This is a hack, not a good solution. The best solution has the most upvotes in this question. Why not use that one? It is better in every regard. – usr May 31 '12 at 14:45
  • -1, as it doesn't work with scientific notation, "-1" or "1.0" – Chango May 31 '12 at 14:47
17

ES6:

Number.isInteger(Number(theNumberString)) && Number(theNumberString) > 0
oma
  • 38,642
  • 11
  • 71
  • 99
5

This is almost a duplicate question fo this one:

Validate decimal numbers in JavaScript - IsNumeric()

It's answer is:

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

so, a positive integer would be:

function isPositiveInteger(n) {
  var floatN = parseFloat(n);
  return !isNaN(floatN) && isFinite(n) && floatN > 0
      && floatN % 1 == 0;
}
Community
  • 1
  • 1
Chango
  • 6,754
  • 1
  • 28
  • 37
  • I know it's almost a duplicate to that 'Validate numbers in JavaScript' question, I read through that whole thing, but I thought a question specifically about string representations of integers deserved it's own page. – Mick Byrne Jun 04 '12 at 12:48
5

This is how I validate that a string is a positive integer.

var str = "123";
var str1 = "1.5";
var str2 = "-123";

console.log("is str positive integer: ", Number.isInteger(Number(str)) && Number(str) > 0)
console.log("is str1 positive integer: ", Number.isInteger(Number(str1)) && Number(str1) > 0)
console.log("is str2 positive integer: ", Number.isInteger(Number(str2)) && Number(str2) > 0)
Md. Robi Ullah
  • 1,703
  • 3
  • 20
  • 32
4

Simple

function isInteger(num) {
  return (num ^ 0) === num;
}

console.log(isInteger(1));

You can also extend Number and assign the function to it via prototype.

manish kumar
  • 4,412
  • 4
  • 34
  • 51
  • 1
    Very nice method, but note that this will only be valid up to 2147483647 (32 bit Integer) – Fini Sep 23 '22 at 15:58
2
return ((parseInt(str, 10).toString() == str) && str.indexOf('-') === -1);

won't work if you give a string like '0001' though

Sebas
  • 21,192
  • 9
  • 55
  • 109
2

My function checks if number is +ve and could be have decimal value as well.

       function validateNumeric(numValue){
            var value = parseFloat(numValue);
            if (!numValue.toString().match(/^[-]?\d*\.?\d*$/)) 
                    return false;
            else if (numValue < 0) {
                return false;
            }
            return true;        
        }
Deepak Nirala
  • 826
  • 6
  • 11
2

Just to build on VisioN's answer above, if you are using the jQuery validation plugin you could use this:

$(document).ready(function() {
    $.validator.addMethod('integer', function(value, element, param) {
        return (value >>> 0 === parseFloat(value) && value > 0);
    }, 'Please enter a non zero integer value!');
}

Then you could use in your normal rules set or add it dynamically this way:

$("#positiveIntegerField").rules("add", {required:true, integer:true});
1

If you are using HTML5 forms, you can use attribute min="0" for form element <input type="number" />. This is supported by all major browsers. It does not involve Javascript for such simple tasks, but is integrated in new html standard. It is documented on https://www.w3schools.com/tags/att_input_min.asp

mggluscevic
  • 431
  • 5
  • 8
1

Most of the time you need this type of check for database usage, like checking if string valid userId. Bacause of that there can't be any strange symbols that can be parced as integer. Also integer should be in database range of integer. You just need normal int like 1,2,3 and so on.

const isStrNormPosInt = (str: string) => {
  return /^([1-9]\d*)$/.test(str) && Number(str) <= 2147483647 // postgres max int
}

If check is passed, you can just convert it to number Number(str)

ZiiMakc
  • 31,187
  • 24
  • 65
  • 105
1

My criteria needed some additional checking built on top of @VisioN's answer.

  • not a negative number - including -0
  • not a float number with a zero decimal part - 0.0, 1.000000
  • not a number in exponential notation - 1e10

I found this useful for validating an Express router's route parameters. e.g. /books/:bookId

Code

/**
 * Validate that a string is a positive integer
 * Excludes float numbers with a zero decimal part, exponential notation and negative 0
 * @param n
 * @returns {boolean}
 */
function isStrictlyPositiveInteger(n) {
    const nString = n.toString(), nInt = parseInt(n), nFloat = parseFloat(n);
    // if a negative number (works on -0)
    if (nString.charAt(0) === '-') {
        return false;
    }
    // if an exponential like 1e10
    if (nString.indexOf('e') > -1) {
        return false;
    }
    // if a float number with a zero decimal part e.g 0.0
    if ((nFloat === nInt) && (nString.indexOf('.') > -1)) {
        return false;
    }
    // if a positive integer
    // https://stackoverflow.com/a/10835227/8470877
    return (0 === n % (!isNaN(nFloat) && 0 <= ~~n));
}

Tests

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e10"                 : false
"1e10"                  : false
"1edf"                  : false
" "                     : false
""                      : false
"0.01"                  : false
"0.00"                  : false
"-0"                    : false
"-0.0"                  : false
"0."                    : false
"-"                     : false
".1"                    : false
Levi
  • 21
  • 4
1

My problem was basically the same: checking if the user has typed a number that is integer and positive.

Our friend Levi's code and response was the best I've found so far on the entire internet and is satisfactory in handling errors.

I had found the following code on the website (https://bobbyhadz.com/blog/javascript-check-if-string-is-positive-integer):

function isPositiveInteger(str) {
   if (typeof str !== 'string') {
     return false;
   }
   const num = Number(str);
   if (Number.isInteger(num) && num > 0) {
     return true;
   }
   return false;
}

However, for 1e10 it doesn't return correctly.

Ether Man
  • 41
  • 6
  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/late-answers/32448871) – mymiracl Aug 16 '22 at 12:13
  • How can you not answer the question? I strengthened the friend's answer and even commented on the error handling in the other code, which is one of the best known. My effort to help colleagues was sincere. – Ether Man Aug 20 '22 at 04:41
0

(~~a == a) where a is the string.

Uyghur Lives Matter
  • 18,820
  • 42
  • 108
  • 144
Gus
  • 596
  • 4
  • 7