Fastest way to check if a JS variable starts with a number - javascript

I am using an object as a hash table and I have stuffed both regular properties and integers as keys into it.
I am now interested in counting the number of keys in this object which are numbers, though obviously a for (x in obj) { if (typeof x === "number") { ... } } will not produce the result I want because all keys are strings.
Therefore I determined that it is sufficient for my purposes to assume that if a key's first character is a number then it must be a number so I am not concerned if key "3a" is "wrongly" determined to be a number.
Given this relaxation I think i can just check it like this
for (x in obj) {
var charCode = x.charCodeAt(0);
if (charCode < 58 && charCode > 47) { // ascii digits check
...
}
}
thereby avoiding a regex and parseInt and such.
Will this work? charCodeAt is JS 1.2 so this should be bullet-proof, yes?
Hint: I would love to see a jsperf comparing my function with what everyone comes up with. :) I'd do it myself but jsperf confuses me
Update: Thanks for starting up the JSPerf, it confirms my hope that the charCodeAt function would be executing a very quick piece of code reading out the int value of a character. The other approaches involve parsing.

parseInt(x, 10) will correctly parse a leading positive or negative number from a string, so try this:
function startsWithNumber(x) {
return !isNaN(parseInt(x, 10));
}
startsWithNumber('123abc'); // true
startsWithNumber('-123abc'); // true
startsWithNumber('123'); // true
startsWithNumber('-123'); // true
startsWithNumber(123); // true
startsWithNumber(-123); // true
startsWithNumber('abc'); // false
startsWithNumber('-abc'); // false
startsWithNumber('abc123'); // false
startsWithNumber('-abc123'); // false

Why speculate when you can measure. On Chrome, your method appears to be the fastest. The proposed alternatives all come at about 60% behind on my test runs.

The question is misleading because it is hard to tell this of a variable's name but in the example you're dealing with object properties (which are some kind of variables of course...). In this case, if you only need to know if it starts with a number, probably the best choice is parseInt. It will return NaN for any string that doesn't start with a number.

You could also use isNaN(x) or isFinite(x) - see this SO question

Related

JS Regex says Number contains a '.' but there is none

On this page, https://emperorbob7.github.io/JSheets/, I have a function called TYPE, syntax for it is linked on the page, the RegEx used for the decimal detection function is located in codeblock below*
Once I put in too many numbers however, the TYPE says the cell contains a decimal despite none being there. Is this an automatic function that adds a . whenever a number exceeds a certain limit?
Example case: 3123123123123123123122312312312
Output: Decimal
Edit:
function TYPE() {
const regex = /\.[0-9]/;
if(arguments[0] == "true" || arguments[0] == "false")
return "Boolean";
if(isNaN(arguments[0]))
return "String";
else if(regex.test(arguments[0]))
return "Decimal";
else
return "Integer";
}
Code^ Sorry for not posting it before, will keep it in mind for the future.
Sorry for the badly worded question, thanks in advance
You have an integer that is larger than the Number object can handle (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER), so when it's converted to a string for the regex it becomes an exponential value.
Try console.log(3123123123123123123122312312312); and you will get 3.123123123123123e+30
Or
let val = 3123123123123123123122312312312;
val.toString();
"3.123123123123123e+30"
You can also test your value with Number.isSafeInteger(3123123123123123123122312312312);, which returns false.
The solution is to use Number.isInteger(); as your test instead of a regex. It correctly returns true for your large number.
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
Javascript can only store integers up to 9007199254740991 safely. Beyond that, javascript may convert the number to power notation (i.e., 5000000000000000000000 becomes 5e+21) or start converting digits into zeros for storage.
var n = 3123123123123123123122312312312;
console.log(n);
/* Output: 3.123123123123123e+30 */
You can use Number.isSafeInteger() to test whether the number is within the safe range, and then apply your original code to it in that case. If not, you can perform a different test (such as a test against /\d\.\d+e\+\d+ ) to see whether the decimal included is due to exponent notation.
Also be aware that a number in exponent notation will test true using Number.isInteger(), even if it was a floating point to begin with, as that information will be lost.
var x_int = 3123123123123123123122312312312;
var x_flt = 3123123123123123123122312312312.333333333;
console.log( x_int === x_flt);
/* Output: true */
console.log(Number.isInteger(x_flt));
/* Output: true */

Javascript equality triple equals but what about greater than and less than?

I was explaining to a colleague that you should use === and !== (and >== and <== of course) when comparing variables in JavaScript so that it doesn't coerce the arguments and get all froopy and confusing but they asked me a two part question that I did not know the answer to and thought I would ask the experts here, specifically it is:
What about > and < - when they compare do they also coerce the arguments or not - why isn't there some sort of >> and << operator (probably need to be some other syntax as I would guess they would be bit shift operators if it is going along the whole C style but you get the gist)?
So I can write a test to find the answer to the first part, which I did, here it is:
// Demo the difference between == and ===
alert(5 == "5");
alert(5 === "5");
// Check out what happens with >
alert(5 > "4");
alert(5 > 4);
and it returned:
true
false
true
true
so it does look like the > is doing the coercion since > "4" and > 4 return the same result. so how about the second part...
Is there some sort of operator for > and < that do not coerce the type (or how can I change my test to perform the test safely)?
No, there's no need for such operators. The type checking done for those relational operators is different than for equality and inequality. (edit — perhaps it's a little strong to say that there's "no need"; that's true only because JavaScript deems it so :-)
Specifically, the > and < and >= and <= operators all operate either on two numeric values, or two strings, preferring numeric values. That is, if one value is a number, then the other is treated as a number. If a non-number can't be cleanly converted to a number (that is, if it ends up as NaN), then the result of the comparison is undefined. (That's a little problematic, because undefined will look like false in the context of an if statement.)
If both values are strings, then a collating-order string comparison is performed instead.
If you think about it, these comparisons don't make any sense for object instances; what does it mean for an object to be "greater than" another? I suppose, perhaps, that this means that if you're finding yourself with values of variant types being compared like this, and that's causing problems, then yes you have to detect the situation yourself. It seems to me that it would be good to work upstream and think about whether there's something fishy about the code that's leading to such a situation.
Is there some sort of operator for > and < that do not coerce the type
No.
how can I change my test to perform the test safely
You would have to explicitly test the types:
typeof a === typeof b && a > b
I referenced Flanagan's JavaScript: The Definitive Guide (5th Ed.) and there does not seem to be non-coercive comparison operators.
You are right in saying the << and >> are indeed bitwise operators so that wouldn't work.
I would suggest you deliberately coerce the values youself:
var num_as_string = '4';
var num = +num_as_string;
if (5 > num) { ... }
12 > '100' // false
'12' > 100 // false
'12' > '100' // true
As others mentioned, if one is a number the other is casted to a number. Same rule applies to these cases as well:
null > 0 // false
null < 0 // false
null >= 0 // true
However, there might be cases that you would need null >= 0 to give false (or any of the number string comparison cases above), therefore it is indeed a need to have strict comparison >== or <==.
For example, I am writing a compareFunction for the Array.prototype.sort() and an expression like x>=0 would treat null values like 0's and put them together, whereas I want to put them elsewhere. I have to write extra logic for those cases.
Javascript says deal with it on your own (in practice).

JavaScript Number preserve leading 0

I have a problem, I build a very simple javascript search for postal codes.
I am using JS Numbers because I want to check if the passed number (search term) is less||equal or more||equal to the max and min.
value >= splitZips[0] && value <= splitZips[1]
But the Javascript Number var type deletes leading 0, which is a problem because I have postal codes like 01075 and also postal codes like 8430. So it can not find the small 4 digit codes.
Any idea how to fix this?
Represent them as a String. Outside of strict mode, a leading zero denotes an octal number otherwise.
Also, why would a leading zero have any significance when calculating numbers? Just use parseInt(num, 10) if you need to.
Store and display the postcodes as strings, thus retaining the leading zeros. If you need to make a numerical comparison convert to number at the time. The easiest way to convert is with the unary plus operator:
var strPC = "01745",
numPC = +strPC;
alert(numPC === +"01745"); // true
+value >= +splitZips[0] && +value <= +splitZips[1];
// etc.
Before you start comparing you might want to ensure the entered value actually is numeric - an easy way to be sure it is a four or five digit code with or without leading zeros is with a regex:
/^\d{4,5}$/.test(searchTerm) // returns true or false
Instead a parseInt you could use type casting :)
"0123">"122" // false
+"0123">"122" // true | that means: 123>"122"
Btw, what more you can use a each of bitwise operators :
~~"0123"
"0123"|0
"0123"&"0123"
"0123">>0
"0123"<<0
With the same effect :)

Compare phone numbers in JavaScript

I have an array of phone numbers and I need to find if a particular phone number is in it.
What I tried doing at first was if(arr.indexOf(phoneNumber) != -1) { bla.. }. And it worked - sometimes.
I later discovered that since the number/s would arrive from different phones/entry forms, some people use country codes (like +1-xxx-xxx-xxxx), some wouldn't. Some use spaces as seperators and some just put in 10 digits in a row. In short - hell to compare.
What I need is an elegant solution that would allow me to compare, hopefully without having to replicate or change the original array.
In C++ you can define comparison operators. I envision my solution as something like this pseudo-code, hopefully using some smart regex:
function phoneNumberCompare(a, b) {
a = removeAllSeperators(a); //regex??
a = a.substring(a.length, a.length - 10);
b = removeAllSeperators(b); //regex??
b = b.substring(b.length, b.length - 10);
return (a < b ? -1 : (a == b ? 0 : 1)); //comaprison in C++ returns -1, 0, 1
}
and use it like if(arr.indexOf(phoneNumber, phoneNumberCompare) != -1)
Now, I know a solution like this construct does not exist in JavaScript, but can someone suggest something short and elegant that achieves the desired result?
As always, thanks for your time.
PS: I know indexOf() already has a second parameter (position), the above is just ment to illustrate what I need.
You really should sanitize all the data, both at collection and in the DB.
But for now, here's what you asked for:
function bPhoneNumberInArray (targetNum, numArray) {
var targSanitized = targetNum.replace (/[^\d]/g, "")
.replace (/^.*(\d{10})$/, "$1");
//--- Choose a character that is unlikely to ever be in a valid entry.
var arraySanitized = numArray.join ('Á').replace (/[^\dÁ]/g, "") + 'Á';
//--- Only matches numbers that END with the target 10 digits.
return (new RegExp (targSanitized + 'Á') ).test (arraySanitized);
}
How it works:
The first statement removes everything but digits (0-9) from the target number and then strips out anything before the last 10 digits.
Then we convert the array to be searched into a string (very fast operation).
When joining the array, we use some character to separate each entry.
It must be a character that we are reasonably sure would never appear in the array. In this case we chose Á. It could be anything that doesn't ever appear in the array.
So, an array: [11, 22, 33] becomes a string: 11Á22Á33Á, for example.
The final regex, then searches for the target number immediately followed by our marker-character -- which signals the end of each entry. This ensures that only the last 10 digits of an array's number are checked against the 10-digit target.
Testing:
var numArray = ['0132456789', "+14568794324", "123-456-7890"];
bPhoneNumberInArray ("+1-456-879-4324", numArray) // true
bPhoneNumberInArray ("+14568794324", numArray) // true
bPhoneNumberInArray ("4568794324", numArray) // true
bPhoneNumberInArray ("+145 XXX !! 68794324", numArray) // true !
bPhoneNumberInArray ("+1-666-879-4324", numArray) // false
You should sanitize both the input and all array values, to make sure they conform to the same ruleset.
Just create a function called sanitizePhonenumber, where you strip (or add, depending on your preferences) the country code and all other signs you dont want there.
After that you can just compare them as you are doing now.

Convert an entire String into an Integer in JavaScript

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?
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 :)
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.
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).
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.

Categories

Resources