39

During some projects I have needed to validate some data and be as certain as possible that it is javascript numerical value that can be used in mathematical operations.

jQuery, and some other javascript libraries already include such a function, usually called isNumeric. There is also a post on stackoverflow that has been widely accepted as the answer, the same general routine that the fore mentioned libraries are using.

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

Being my first post, I was unable to reply in that thread. The issue that I had with the accepted post was that there appear to be some corner cases that affected some work that I was doing, and so I made some changes to try and cover the issue I was having.

First, the code above would return true if the argument was an array of length 1, and that single element was of a type deemed as numeric by the above logic. In my opinion, if it's an array then its not numeric.

To alleviate this problem, I added a check to discount arrays from the logic

function isNumber(n) {
  return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n);
}

Of course, you could also use Array.isArray instead of Object.prototype.toString.call(n) !== '[object Array]'

EDIT: I've changed the code to reflect a generic test for array, or you could use jquery $.isArray or prototypes Object.isArray

My second issue was that Negative Hexadecimal integer literal strings ("-0xA" -> -10) were not being counted as numeric. However, Positive Hexadecimal integer literal strings ("0xA" -> 10) wrere treated as numeric. I needed both to be valid numeric.

I then modified the logic to take this into account.

function isNumber(n) {
  return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
}

If you are worried about the creation of the regex each time the function is called then you could rewrite it within a closure, something like this

isNumber = (function () {
  var rx = /^-/;

  return function (n) {
      return Object.prototype.toString.call(n) !== '[object Array]' && !isNaN(parseFloat(n)) && isFinite(n.toString().replace(rx, ''));
  };
}());

I then took CMSs +30 test cases and cloned the testing on jsfiddle added my extra test cases and my above described solution.

Everything appears to be working as expected and I haven't experienced any issues. Are there any issues, code or theoretical, that you can see?

It may not replace the widely accepted/used answer but if this is what you are expecting as results from your isNumeric function then hopefully this will be of some help.

EDIT: As pointed out by Bergi, there are other possible objects that could be considered numeric and it would be better to whitelist than blacklist. With this in mind I would add to the criteria.

I want my isNumeric function to consider only Numbers or Strings

With this in mind, it would be better to use

function isNumber(n) {
  return (Object.prototype.toString.call(n) === '[object Number]' || Object.prototype.toString.call(n) === '[object String]') &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
}

This has been added as test 22

Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • 5
    You cannot use `typeof` for testing arrays, `typeof array` returns `"object"`. – Felix Kling Feb 23 '13 at 17:01
  • IMO all algorithms have a scope in which they should be used and which set of arguments it can take. If you ask for **all** possible flaws that may happen, unfortunately you will run into boundaries between what should and should not be "numeric". For example, if I pass in a Date object, can you consider it numeric because its internal ToPrimitive algorithm evaluates to an integer? That is similar to the logic applied to your numeric strings apparently. I see that `Date` objects should be false in your unit tests, that was just an example. – Fabrício Matté Feb 23 '13 at 17:04
  • 11
    This should be migrated to http://codereview.stackexchange.com, or post an answer to that question. – the system Feb 23 '13 at 17:07
  • 1
    @thesystem Unfortunately the OP was not able to post an answer to that question because it's "protected" (requires 10 rep to answer) and this is Xotic750's first post. It *could* fit on codereview I assume (I'm not active there so I'm not 100% sure), but is this not on-topic here? – Wesley Murch Feb 23 '13 at 17:29
  • @WesleyMurch: If the question is *"Are there any issues, code or theoretical, that you can see?"*, then to me it seems to be a code review situation. But no sweat if others disagree. :-) – the system Feb 23 '13 at 17:32
  • 4
    @thesystem I think you may be right, there isn't really a question here besides "this passed all my tests, what do you guys think?". Maybe this should be closed and posted as an answer to http://stackoverflow.com/q/18082/398242 – Wesley Murch Feb 23 '13 at 17:53
  • @Xotic750: I personally suggest posting this as an answer to the original question (with a little rewording to make it more of an "answer"), you've got enough rep now. It will be more visible there as well. I'm voting to close. – Wesley Murch Feb 23 '13 at 17:57
  • 1
    @Wesley Munch: I will post it there with modified wording now that I have that ability. – Xotic750 Feb 23 '13 at 18:15
  • @Xotic750 When you do, please flag your question for closure (click the "flag" link beneath your post). Just use the "other" option and give a brief explanation. – Wesley Murch Feb 23 '13 at 18:16
  • It has now been flagged. – Xotic750 Feb 23 '13 at 18:29
  • 1
    @Xotic750 Welcome to SO, nice to have a coherent new member :) – Wesley Murch Feb 23 '13 at 18:52
  • FYI You might already be aware of this, but Array.isArray is part of ECMAScript 5 and thus won't work in older browsers. – Matt Browne Mar 06 '13 at 04:03
  • @Matt B. I am aware and hence it was not used, just left as a note that you could use it (if available) – Xotic750 Mar 06 '13 at 08:57
  • constructor.name will get you 'Array' if old IE isn't a concern. – Erik Reppen Mar 07 '13 at 06:30

7 Answers7

4

In my opinion, if it's an array then its not numeric. To alleviate this problem, I added a check to discount arrays from the logic

You can have that problem with any other object as well, for example {toString:function(){return "1.2";}}. Which objects would you think were numeric? Number objects? None?

Instead of trying to blacklist some things that fail your test, you should explicitly whitelist the things you want to be numeric. What is your function supposed to get, primitive strings and numbers? Then test exactly for them:

(typeof n == "string" || typeof n == "number")
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • That is a reasonable point and in which case then I would change my criteria to be: if the object is a number or a string – Xotic750 Mar 06 '13 at 08:56
1

If it's OK for you to use regular expressions, this might do the trick:

function (n) 
    { 
    return (Object.prototype.toString.call(n) === '[object Number]' ||
            Object.prototype.toString.call(n) === '[object String]') && 
           (typeof(n) != 'undefined')  &&  (n!=null) && 
           (/^-?\d+((.\d)?\d*(e[-]?\d)?(\d)*)$/.test(n.toString()) ||
           /^-?0x[0-9A-F]+$/.test(n.toString()));
    }

edit: Fixed problem with hex numbers

Rembunator
  • 1,305
  • 7
  • 13
  • I think the only way to be certain that javascript doesn't convert the values, is by looking at the value literally. Using the same rules humans do evaluating numbers. – Rembunator Mar 08 '13 at 08:14
0
function isNumber(value){return typeof value == 'number';}
Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
Tutan Ramen
  • 1,234
  • 1
  • 8
  • 27
0

If AMD sounds good to you, have a look at mout's isNumber().

  • Haven't tested the above library and function so have no idea how it fairs on the supplied tests, but I think it may be a little unnecessary to include a whole library for such a function. – Xotic750 Mar 02 '13 at 19:45
  • 1
    Internally this is just using `Object.prototype.toString.call(val)` and checking if it's == "[object Number]"...so unfortunately wouldn't help if you had a *string* (rather than a number) and you weren't sure if it was numeric or not, which I believe is what the OP wants. – Matt Browne Mar 06 '13 at 04:10
  • @Xotic750 You don't need to add the whole library at all. That's what AMD is all about, breaking functionalities into modules so you can load only what you'll really use. –  Mar 14 '13 at 12:33
0

How about:

function isNumber(value) {
  value = Number(value);
  return typeof value === 'number' && !isNaN(value) && isFinite(value);
}
jokeyrhyme
  • 3,258
  • 2
  • 20
  • 20
0

isNaN function used to check the value is a numeric or not. If the values is numeric its return true else returned false.

code:

 <script>

         function IsNumeric(val) {

              if (isNaN(parseFloat(val))) {

                 return false;

          }

          return true

  }


  bool IsNumeric(string);


</script>
0
function isNumber(value){
    return !isNaN(parseFloat(value)) && 
        isFinite(value.toString().replace(/^-/, '')) && 
        typeof value !== 'object';

}

or :

function isNumber(value){
    return !Array.isArray(value) && !isNaN(parseFloat(value)) && 
        isFinite(value.toString().replace(/^-/, '')) && 
        Object.prototype.toString.call(value) !== '[object Object]';
}
Akram Berkawy
  • 4,920
  • 2
  • 19
  • 27