Compare phone numbers in JavaScript - 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.

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 */

How do I replace a letter that is 15 chars ahead of each other?

I am trying to replace each character a user inputs that is 15 letters ahead in the alphabet. For example, if a user inputs the word A, then it would output "P".
the word "AB" would output "PQ" and so on with every word.
I've tried the following code but it is not working. I am thinking a loop may be the answer, but if anyone can think of some better ideas let me know.
alphabetArray = ["abcdefghijklmnopqrstuvwyxz"];
function jumpAhead15(aString){
aString.replace(alphabetArray[0][aString + 15]);
}
jumpAhead15("hi");
You could take the string as iterable and get an array of characters and replace any character by getting the index and the offset, adjusted by the remainder of the length of the alphabet.
The reminder operator % returns the rest of a division:
The remainder operator returns the remainder left over when one operand is divided by a second operand. It always takes the sign of the dividend.
The function is here to keep the index for the getting the character of alphabet in a valid range. For example by taking index 20 and the wanted shift of 15, you get 35 which is not an index of alphabet. by using the remainder operator you get the value of 9 which is the rest of 35 / 26 = 1 rest 9
Then return the character and join the array to a final string.
Method used:
Array.from, which takes two arguments, one iterable or an object with a length property and a mappring function, which is called for every element of the new array. (c is the first parameter of the callback and denotes here a single character)
arrow function as callback for Array.from
function jumpAhead15(aString) {
var alphabet = "abcdefghijklmnopqrstuvwyxz";
return Array
.from(
aString,
c => alphabet[(alphabet.indexOf(c) + 15) % alphabet.length]
)
.join('');
}
console.log(jumpAhead15("hi"));
console.log(jumpAhead15("ab"));
With alphabetArray as above, try:
aString.split("").map(letter => alphabetArray[(alphabetArray.indexOf(letter) + 15) % 26]).join("");
That is: convert the string to an array of 1-letter strings, replace each letter by the one 15 places further along (wrapping if necessary, hence the modulus operator %), then join the letters back together into one string.
If you find the answer from Nina hard to understand, then this one may not be for you either, but here is a function that is built on a more generic letter rotation scheme. It also handles differences in case:
const rotate = (configs) => (str) => str.split('').map(
c => configs.reduce(
(s, [letters, offset]) => {
const idx = letters.indexOf(s);
return idx < 0 ? s : letters[(idx + offset) % letters.length]
},
c
)
).join('')
const by15 = rotate([
[[...'abcdefghijklmnopqrstuvwxyz'], 15],
[[...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'], 15]
])
console.log(by15('ab')) //~> 'pq'
console.log(by15('aB')) //~> 'pQ'
console.log(by15('Hello, world!')) //~> 'Wtaad, ldgas!'
This uses the same basic technique with the modulus operator, modified slightly to return the original character if it's not in our list. We do this for each alphabet supplied, in this case, the upper- and lower-case letters. This only works if the different alphabets have no overlap. We pass the offset together with each alphabet, in case you want to, for instance, rotate digits by 3 and letters by 15. If you didn't want to do this, you could move the offset out of the configs parameter (in which case it might be called alphabets) and make the single offset a second parameter for rotate.
Note the structure: rotate is an arrow function which returns an arrow function. So to get a useful function out of it, you need to call it with the alphabet configuration. The function returned just takes a string and returns the rotated string.
This is not the most efficient solution, as you have to loop through each alphabet, even once you've found it. Since I wouldn't expect it to be used with many alphabets, this seems a good tradeoff for relatively clean code. But one could easily write a version which searched through until it found an alphabet that contained your character and then worked on that one. This is left as an exercise for the reader.

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

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

What is the difference between Number(...) and parseFloat(...)

What is the difference between parseInt(string) and Number(string) in JavaScript has been asked previously.
But the answers basically focused on the radix and the ability of parseInt to take a string like "123htg" and turn it into 123.
What I am asking here is if there is any big difference between the returns of Number(...) and parseFloat(...) when you pass it an actual number string with no radix at all.
The internal workings are not that different, as #James Allardic already answered. There is a difference though. Using parseFloat, a (trimmed) string starting with one or more numeric characters followed by alphanumeric characters can convert to a Number, with Number that will not succeed. As in:
parseFloat('3.23abc'); //=> 3.23
Number('3.23abc'); //=> NaN
In both conversions, the input string is trimmed, by the way:
parseFloat(' 3.23abc '); //=> 3.23
Number(' 3.23 '); //=> 3.23
No. Both will result in the internal ToNumber(string) function being called.
From ES5 section 15.7.1 (The Number Constructor Called as a Function):
When Number is called as a function rather than as a constructor, it performs a type conversion...
Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else returns +0.
From ES5 section 15.1.2.3 (parseFloat (string)):
...
If neither trimmedString nor any prefix of trimmedString satisfies the syntax of a StrDecimalLiteral (see 9.3.1)
...
And 9.3.1 is the section titled "ToNumber Applied to the String Type", which is what the first quote is referring to when it says ToNumber(value).
Update (see comments)
By calling the Number constructor with the new operator, you will get an instance of the Number object, rather than a numeric literal. For example:
typeof new Number(10); //object
typeof Number(10); //number
This is defined in section 15.7.2 (The Number Constructor):
When Number is called as part of a new expression it is a constructor: it initialises the newly created object.
Not a whole lot of difference, as long as you're sure there's nothing but digits in your string. If there are, Number will return NaN. Another problem that you might get using the Number constructor is that co-workers might think you forgot the new keyword, and add it later on, causing strict comparisons to fail new Number(123) === 123 --> false whereas Number(123) === 123 --> true.
In general, I prefer to leave the Number constructor for what it is, and just use the shortest syntax there is to cast to an int/float: +numString, or use parse*.
When not using new to create a wrapper object for a numerical value, Number is relegated to simply doing type conversion from string to number.
'parseFloat' on the other hand, as you mentioned, can parse a floating point number from any string that starts with a digit, a decimal, or +/-
So, if you're only working with strings that contain only numerical values, Number(x) and parseFloat(x) will result in the same values
Please excuse me posting yet another answer, but I just got here via a Google search and did not find all of the details that I wanted. Running the following code in Node.js:
var vals = ["1", "1.1", "0", "1.1abc", "", " ", null];
for(var i = 0; i < vals.length; i++){
var ifTest = false;
if(vals[i])
{
ifTest = true;
}
console.log("val=" + vals[i] + ", Number()=" + Number(vals[i])+ ", parseFloat()=" + parseFloat(vals[i]) + ", if()=" + ifTest);
}
gives the following output:
val=1, Number()=1, parseFloat()=1, if()=true
val=1.1, Number()=1.1, parseFloat()=1.1, if()=true
val=0, Number()=0, parseFloat()=0, if()=true
val=1.1abc, Number()=NaN, parseFloat()=1.1, if()=true
val=, Number()=0, parseFloat()=NaN, if()=false
val= , Number()=0, parseFloat()=NaN, if()=true
val=null, Number()=0, parseFloat()=NaN, if()=false
Some noteworthy takeaways:
If protecting with an if(val) before trying to convert to number, then parseFloat() will return a number except in the whitespace case.
Number returns a number in all cases except for non-numeric characters aside from white-space.
Please feel free to add any test cases that I may be missing.

Finding an element in an array

4,5,6,7];
pin=3;
We got to search pin in hay.
Conventionally we loop through hay and check for pin( assume there is no native function called array.indexOf ).
How about,
hay=hay.join(",");
pin=","+pin+",";
index=hay.indexOf(pin);
Any Suggestions please?
Consider hay of [2,3,4] and a pin of 2... you'll be looking for ",2," in a string "2,3,4". Now you could add commas to the start and end of hay as well, but it's a bit ugly, isn't it?
There's then the problem of strings having variable lengths: consider an array of [1,222222,3,4]. When you look for 3, you'll end up with an inappropriate index because of the length of "222222". (Even in the case of only single digit values, you'll need to divide by 3.)
You've then potentially got problems when you start moving from integers to decimal values, which may be formatted differently in different cultures - possibly using a comma as the decimal separator. I don't know whether JavaScript uses the culture-specific separator by default, but that's part of the problem - you're suddenly having to consider aspects of the language/platform which have nothing to do with the task at hand.
Generally speaking, converting data into a string format in order to do something which doesn't really depend on the string format is a bad idea. It would be better to write a general-purpose indexOf method to perform the looping for you. (I'd be surprised if such a method didn't already exist, to be honest, but it's easy enough to write once and reuse if you need to.)
Heck, assume there is no string indexOf, either.
var A=[11,7,9,1,17,13,19,18,10,6,3,8,2,5,4,14,20,15,16,12],
L=A.length, n=3;
while(L>-1 && A[--L]!==n);
alert(L)
You don't need to use string in the middle, you can just loop through your array, if I understand your question right.
var hay = [1, 2, 3, 'Whoa', 'wheee', 5, 'needle'], needle = 'needle';
for ( var i = 0, len = hay.length; i < len; i += 1 ) {
if ( hay[ i ] === needle ) {
alert( "hay’s element number " + i + " is the needle!" );
break;
}
}

Categories

Resources