Phone mask for text field with regex - javascript

I'm using this function to phone mask and works almost perfectly.
function mask(o, f)
{
v_obj = o;
v_fun = f;
setTimeout("execmask()", 1)
};
function execmask()
{
v_obj.value = v_fun(v_obj.value)
};
function mphone(v){
v=v.replace(/\D/g,"");
v=v.substring(0, 11);
v=v.replace(/^(\d{2})(\d)/g,"(OXX$1) $2");
v=v.replace(/(\d)(\d{4})$/,"$1-$2");
return v;
}
Here I run the mask in the text field:
<input type="text" id="phone" name="phone" onkeypress="mask(this, mphone);" onblur="mask(this, mphone);" />
The problem is that I need to change this part of the code (OXX$1) to (0XX$1).
Current situation:
No. Of Digits
Input Field
9 digit
(OXX99) 99999-9999
8 digit
(OXX99) 9999-9999
The correct formatting that I need:
No. Of Digits
Input Field
9 digit
(0XX99) 99999-9999
8 digit
(0XX99) 9999-9999
The amount of 8 or 9 digits is the choice of the user.
Changing O to 0, causes an error in the mask.

function mask(o, f) {
setTimeout(function () {
var v = f(o.value);
if (v != o.value) {
o.value = v;
}
}, 1);
}
function mphone(v) {
var r = v.replace(/\D/g,"");
r = r.replace(/^0/,"");
if (r.length > 10) {
// 11+ digits. Format as 5+4.
r = r.replace(/^(\d\d)(\d{5})(\d{4}).*/,"(0XX$1) $2-$3");
}
else if (r.length > 5) {
// 6..10 digits. Format as 4+4
r = r.replace(/^(\d\d)(\d{4})(\d{0,4}).*/,"(0XX$1) $2-$3");
}
else if (r.length > 2) {
// 3..5 digits. Add (0XX..)
r = r.replace(/^(\d\d)(\d{0,5})/,"(0XX$1) $2");
}
else {
// 0..2 digits. Just add (0XX
r = r.replace(/^(\d*)/, "(0XX$1");
}
return r;
}
http://jsfiddle.net/BBeWN/

In this project you can see the use of several methods below, including the phone mask and applying them to an input:
Repository: react-text-field-mask
Demo sandbox: https://codesandbox.io/s/eager-monad-qzod32
Regex masks:
// (00) 00000-0000
const phoneMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue
.replace(/\D/g, '')
.replace(/(\d{2})(\d)/, '($1) $2')
.replace(/(\d{5})(\d{4})/, '$1-$2');
};
// (00) 0000-0000
const landlineTelephoneMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue
.replace(/\D/g, '')
.replace(/(\d{2})(\d)/, '($1) $2')
.replace(/(\d{4})(\d{4})/, '$1-$2');
};
// 10.000,00 -> 10000.00
const removeMoneyMask = (value: string | number) => {
const originalNumber = Number(value);
const numberIsWithMask = Number.isNaN(originalNumber);
if (numberIsWithMask) {
const stringValue = value.toString();
const splitedMoneyMask = stringValue
.split('.')
.join('')
.split(',')
.join('.');
return splitedMoneyMask.replace(/[^0-9.-]+/g, '');
}
return value.toString();
};
type BehaviorMode = 'standard' | 'typing';
const moneyMaskCustomization = {
/**
* typingMode - The value is typed from right to left:
* - '1' -> '0.01'
* - '10' -> '0.10'
*
* defaultMode - Simple conversion:
* - '1' -> '1.00'
* - '10' -> '10.00'
*/
maskBehaviorMode: (behaviorMode: BehaviorMode, value: string | number) => {
const numberWithoutMask = removeMoneyMask(value);
const normalizedMoneyValue = moneyMaskCustomization.normalizeMoneyValue(
numberWithoutMask.toString(),
);
const integerWithoutDecimalPlaces = normalizedMoneyValue.length === 1;
if (behaviorMode === 'typing' && integerWithoutDecimalPlaces) {
const newNumberFormat = Number(normalizedMoneyValue) / 100;
return Number(newNumberFormat).toFixed(2);
}
if (behaviorMode === 'standard' && integerWithoutDecimalPlaces) {
return Number(normalizedMoneyValue).toFixed(2);
}
return normalizedMoneyValue;
},
normalizeMoneyValue: (numberToNormalized: string) => {
const [stringInteger, stringDecimal] = numberToNormalized.split('.');
if (stringDecimal && stringDecimal.length === 1) {
const lastPositionOfTheInteger = stringInteger.length - 1;
const lastToIntegerPlace = stringInteger[lastPositionOfTheInteger];
if (lastPositionOfTheInteger !== 0) {
const firstIntegerPlace = stringInteger.substring(
0,
lastPositionOfTheInteger,
);
return `${firstIntegerPlace}.${lastToIntegerPlace}${stringDecimal}`;
}
return `${0}.${lastToIntegerPlace}${stringDecimal}`;
}
if (stringDecimal && stringDecimal.length === 3) {
const firstDecimalPlace = stringDecimal.substring(0, 1);
const lastTwoDecimalPlaces = stringDecimal.substring(1, 3);
const integerIsZero = Number(stringInteger) === 0;
if (integerIsZero) {
return `${firstDecimalPlace}.${lastTwoDecimalPlaces}`;
}
return `${stringInteger}${firstDecimalPlace}.${lastTwoDecimalPlaces}`;
}
return numberToNormalized;
},
};
// 10.000,00
const moneyMask = (
value: string | number,
maskBehaviorMode: BehaviorMode = 'standard',
) => {
if (value || Number.isInteger(value)) {
const moneyValue = moneyMaskCustomization.maskBehaviorMode(
maskBehaviorMode,
value,
);
return moneyValue
.replace(/\D/g, '')
.replace(/\D/g, '.')
.replace(/(\d)(\d{2})$/, '$1,$2')
.replace(/(?=(\d{3})+(\D))\B/g, '.');
}
return '';
};
// 000.000.000-00
const cpfMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue
.replace(/\D/g, '')
.replace(/(\d{3})(\d)/, '$1.$2')
.replace(/(\d{3})(\d)/, '$1.$2')
.replace(/(\d{3})(\d{1,2})/, '$1-$2')
.replace(/(-\d{2})\d+?$/, '$1');
};
// 00.000.000/0000-000
const cnpjMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue
.replace(/\D/g, '')
.replace(/(\d{2})(\d)/, '$1.$2')
.replace(/(\d{3})(\d)/, '$1.$2')
.replace(/(\d{3})(\d)/, '$1/$2')
.replace(/(\d{4})(\d{2})/, '$1-$2');
};
// 000.000.000-00 or 00.000.000/0000-000
const cpfOrCnpjMask = (value: string | number) => {
const stringValue = value.toString();
if (stringValue.length >= 15) {
return cnpjMask(value);
}
return cpfMask(value);
};
// 00000-000
const cepMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue.replace(/\D/g, '').replace(/^(\d{5})(\d{3})+?$/, '$1-$2');
};
// 00/00/0000
const dateMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue
.replace(/\D/g, '')
.replace(/(\d{2})(\d)/, '$1/$2')
.replace(/(\d{2})(\d)/, '$1/$2')
.replace(/(\d{4})(\d)/, '$1');
};
const onlyLettersMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue.replace(/[0-9!##¨$%^&*)(+=._-]+/g, '');
};
const onlyNumbersMask = (value: string | number) => {
const stringValue = value.toString();
return stringValue.replace(/\D/g, '');
};

I have made some changes:
I changed the event to keyup (besides the fact that keypress has been deprecated) because keypress always takes the string with one less character and to work I also needed onblur, something that doesn't happen with keyup. Code adjustments were needed to resolve this
and the issue of the "-", that when tried to remove it with backspace it kept always coming back.
fiddle
function mascaraFone(event) {
var valor = document.getElementById("telefone").attributes[0].ownerElement['value'];
var retorno = valor.replace(/\D/g, "");
retorno = retorno.replace(/^0/, "");
if (retorno.length > 10) {
retorno = retorno.replace(/^(\d\d)(\d{5})(\d{4}).*/, "($1) $2-$3");
} else if (retorno.length > 5) {
if (retorno.length == 6 && event.code == "Backspace") {
// necessário pois senão o "-" fica sempre voltando ao dar backspace
return;
}
retorno = retorno.replace(/^(\d\d)(\d{4})(\d{0,4}).*/, "($1) $2-$3");
} else if (retorno.length > 2) {
retorno = retorno.replace(/^(\d\d)(\d{0,5})/, "($1) $2");
} else {
if (retorno.length != 0) {
retorno = retorno.replace(/^(\d*)/, "($1");
}
}
document.getElementById("telefone").attributes[0].ownerElement['value'] = retorno;
}
<input id="telefone" onkeyup="mascaraFone(event)" />

I love this function and I use it all the time. I've added 2 other masks if anyone needs them. I understand that they don't directly answer the question, but they are super useful.
//Social Security Number for USA
function mssn(v) {
var r = v.replace(/\D/g,"");
r = r.replace(/^0/,"");
if (r.length > 9) {
r = r.replace(/^(\d\d\d)(\d{2})(\d{0,4}).*/,"$1-$2-$3");
return r;
}
else if (r.length > 4) {
r = r.replace(/^(\d\d\d)(\d{2})(\d{0,4}).*/,"$1-$2-$3");
}
else if (r.length > 2) {
r = r.replace(/^(\d\d\d)(\d{0,3})/,"$1-$2");
}
else {
r = r.replace(/^(\d*)/, "$1");
}
return r;
}
//USA date
function mdate(v) {
var r = v.replace(/\D/g,"");
if (r.length > 4) {
r = r.replace(/^(\d\d)(\d{2})(\d{0,4}).*/,"$1/$2/$3");
}
else if (r.length > 2) {
r = r.replace(/^(\d\d)(\d{0,2})/,"$1/$2");
}
else if (r.length > 0){
if (r > 12) {
r = "";
}
}
return r;
}

Related

Function that replaces the wrapped word with ampersands with just single hashtag at start

I tried to write a function to replace ampersands which are arround word in string with just single hashtag which will be at the beginning of word &word& => #word. And I did it, but after looking at my code I can tell that it's kind of ugly, and also I need function that will return text to version with ampersands too. So, maybe someone can tell me how can I improve it? Maybe someone can provide version with regex?
const text = "&string& someText&string2& &string3& &string4&&string5&";
const replaceAmpersandsWithHashtag = (text: string) => {
const firstStep = text.replace(/\&/g, '#');
let occurenceOfHashtag = 0;
const secondStep = firstStep.split('').filter(char => {
if(char === '#') {
occurenceOfHashtag += 1 ;
return occurenceOfHashtag % 2 === 0 ? false : true;
}
return true
}).join('');
return secondStep
}
const replaceHashtagWithAmpersands = (text: string) => {
const firstStep = text.replace(/\#/g, '&');
let isLookingForNextAmpersand = false;
const secondStep = firstStep.split('').map((char, index) => {
if(char === '&') {
isLookingForNextAmpersand = true;
return char;
}
if(isLookingForNextAmpersand && firstStep.length === index + 1) {
isLookingForNextAmpersand = false;
return `${char}&`
}
if(isLookingForNextAmpersand && (firstStep.charAt(index + 1) === ' ' || firstStep.charAt(index + 1) === '&')) {
isLookingForNextAmpersand = false;
return `${char}&`
}
return char
}).join('');
return secondStep
}
const textWithHastags = replaceAmpersandsWithHashtag(text);
const againWithAmpersands = replaceHashtagWithAmpersands(textWithHastags);
// Should be "#string someText#string2 #string3 #string4#string5"
console.log(textWithHastags)
// Should be "&string& someText&string2& &string3& &string4&&string5&"
console.log(againWithAmpersands)
// Should be "true"
console.log(text === againWithAmpersands)
Use a capture group in the regexp to copy the word between the & to the replacement.
function replaceAmpersandsWithHashtag(string) {
return string.replace(/&(\w+)&/g, '#$1');
}
function replaceHashtagWithAmpersands(string) {
return string.replace(/#(\w+)/g, '&$1&');
}
const text = "&string& someText&string2& &string3& &string4&&string5&";
const textWithHastags = replaceAmpersandsWithHashtag(text);
const againWithAmpersands = replaceHashtagWithAmpersands(textWithHastags);
// Should be "#string someText#string2 #string3 #string4#string5"
console.log(textWithHastags)
// Should be "&string& someText&string2& &string3& &string4&&string5&"
console.log(againWithAmpersands)
// Should be "true"
console.log(text === againWithAmpersands)
you are overkilling the solution, you can do it easily with the function replaceAll which will replace the occurrences of your search, in this case you will need to use some regex in order to do a proper replacement, so you can use &(\w+)& where the (\w+) part will capture the group between the & and then you can use it in the replace call as $1.
here is your code with the implementation of the explanation above
const text = "&string& someText&string2& &string3& &string4&&string5&";
const replaceAmpersandsWithHashtag = (text) => {
return text.replaceAll(/&(\w+)&/g, '#$1');
}
const replaceHashtagWithAmpersands = (text) => {
return text.replaceAll(/#(\w+)/g, '&$1&');
}
const textWithHastags = replaceAmpersandsWithHashtag(text);
const againWithAmpersands = replaceHashtagWithAmpersands(textWithHastags);
// Should be "#string someText#string2 #string3 #string4#string5"
console.log(textWithHastags)
// Should be "&string& someText&string2& &string3& &string4&&string5&"
console.log(againWithAmpersands)
// Should be "true"
console.log(text === againWithAmpersands)

How to encode Javascript [duplicate]

This question already has answers here:
How to convert a string of hex values to ASCII
(3 answers)
Decode UTF-8 with Javascript
(15 answers)
Closed 1 year ago.
How to decode from:
\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28313\x29
to:
javascript:alert(313)
Does anyone know about coding please guide me, I will appreciate you a lot!
Thank you!
You could write a simple parser that converts Unicode code-points to equivalent characters.
const main = () => {
const convertBtn = document.querySelector('.convert');
convertBtn.addEventListener('click', onConvert);
triggerEvent(convertBtn, 'click');
};
const onConvert = e => {
const input = document.querySelector('.input');
const output = document.querySelector('.output');
output.value = parseUnicodeString(input.value);
};
const parseUnicodeString = unicodeSring => {
let result = '', buffer = null;
for (let i = 0; i < unicodeSring.length; i++) {
if (unicodeSring[i] === 'x') {
buffer = '';
} else if (/[0-9a-f]/.test(unicodeSring[i])) {
if (buffer != null) {
if (buffer.length === 1) {
result += parseCodePoint(buffer + unicodeSring[i]);
buffer = null;
} else {
buffer += unicodeSring[i];
}
} else {
result += unicodeSring[i];
}
}
}
if (buffer != null && buffer.length > 0) {
result += parseCodePoint(buffer);
}
return result;
}
const parseCodePoint = codePoint =>
String.fromCodePoint(parseInt(codePoint, 16));
const triggerEvent = (el, eventName, data) => {
let event;
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
event = new CustomEvent(eventName, {detail: data});
} else {
event = document.createEvent('CustomEvent');
event.initCustomEvent(eventName, true, true, data);
}
el.dispatchEvent(event);
};
main();
body,html{width:100%;height:100%;margin:0;padding:0;background:#000;color:#fff}body{display:flex;flex-direction:column}button{background:#c46;border:thin solid #d26;color:#eee;padding:.125em;font-weight:700;cursor:pointer}button:hover{background:#f47;color:#fff}textarea{flex:1;background:#222;color:#eee;resize:none;border:none;outline:0;padding:.25em}
<textarea class="input">\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28313\x29</textarea>
<button class="convert">Convert</button>
<textarea class="output">
</textarea>
If you combine String.prototype.replace with a regular expression, you can simply the logic above into a one-liner.
const main = () => {
const convertBtn = document.querySelector('.convert');
convertBtn.addEventListener('click', onConvert);
triggerEvent(convertBtn, 'click');
};
const onConvert = e => {
const input = document.querySelector('.input');
const output = document.querySelector('.output');
output.value = parseUnicodeString(input.value);
};
const parseUnicodeString = unicodeSring =>
unicodeSring.replace(/\\x([0-9a-f]{2})/ig, (match, codePoint) =>
String.fromCodePoint(parseInt(codePoint, 16)));
const triggerEvent = (el, eventName, data) => {
let event;
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
event = new CustomEvent(eventName, {detail: data});
} else {
event = document.createEvent('CustomEvent');
event.initCustomEvent(eventName, true, true, data);
}
el.dispatchEvent(event);
};
main();
body,html{width:100%;height:100%;margin:0;padding:0;background:#000;color:#fff}body{display:flex;flex-direction:column}button{background:#c46;border:thin solid #d26;color:#eee;padding:.125em;font-weight:700;cursor:pointer}button:hover{background:#f47;color:#fff}textarea{flex:1;background:#222;color:#eee;resize:none;border:none;outline:0;padding:.25em}
<textarea class="input">\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28313\x29</textarea>
<button class="convert">Convert</button>
<textarea class="output">
</textarea>

Addition of number string in javascript with leading zeros

Following is my code, which is working fine in most scenarios except in case of leading Zeros. It should preserve trailing zeros like -001 + 1 = 002
Code -
function incrementString (str) {
if(str === '') return "1";
if(!str.slice(-1).match(/\d/)) return `${str}1`;
const replacer = x => {
// Check if number
return (parseInt(x) + 1).toString();
}
return str.replace(/\d+/g, replacer )
}
// Return foobar2 which is correct
console.log(incrementString("foobar1"))
// Return foobar100 which is correct
console.log(incrementString("foobar099"))
// Return foobar2 which is incorrect, is should be foobar002
console.log(incrementString("foobar001"))
// Return foobar1 which is incorrect, is should be foobar001
console.log(incrementString("foobar000"))
// Return foobar101 which is incorrect, is should be foobar0101
console.log(incrementString("foobar0100"))
You may use this regex soluton:
function incrementString (str) {
if(str === '') return "1";
if(!str.slice(-1).match(/\d/)) return `${str}1`;
const replacer = (m, g1, g2) => {
// Check if number
var nn = (g1?g1:"") + (parseInt(g2) + 1).toString()
return nn.slice(-1 * m.length)
}
return str.replace(/(0*)(\d+)/g, replacer )
}
// Return foobar2
console.log(incrementString("foobar1"))
// Return foobar100
console.log(incrementString("foobar099"))
// Return foobar002
console.log(incrementString("foobar001"))
// Return foobar001
console.log(incrementString("foobar000"))
// Return foobar0101
console.log(incrementString("foobar0100"))
// Return foobar01000
console.log(incrementString("foobar00999"))
// Return foobar010
console.log(incrementString("foobar009"))
Everything seems to be perfect, you need to only handle the regex part of the leading zeroes in your replacer function.Below is the updated code for the same.
function incrementString(str) {
if (str === '')
return "1";
if (!str.slice(-1).match(/\d/)) {
return `${str}1`;
}
const replacer = x => {
var leadingZerosMatched = x.match(/^0+/);
var incrementedNumber = (parseInt(x) + 1).toString();
var leadingZeroes;
if (leadingZerosMatched && incrementedNumber.length < x.length) {
leadingZeroes = leadingZerosMatched[0];
if(leadingZeroes.length === x.length) {
leadingZeroes = leadingZeroes.slice(0, leadingZeroes.length-1)
}
}
return leadingZeroes ? leadingZeroes + incrementedNumber : incrementedNumber;
}
return str.replace(/\d+/g, replacer)
}
You could split your string from digits and use padStart after increment to preserve leading 0:
const incrementString = (str) => {
const [chars, nums] = str.split(/(\d+)/)
return [
...chars,
String(Number(nums) + 1)
.padStart(nums.length, '0')
].join('')
}
console.log(incrementString("foobar1"))
console.log(incrementString("foobar099"))
console.log(incrementString("foobar001"))
console.log(incrementString("foobar000"))
console.log(incrementString("foobar0100"))
function incrementString (str) {
let [
openingPartial,
terminatingInt
] = str.split(/(\d*)$/);
if (terminatingInt) {
const incrementedInt = String(parseInt(terminatingInt, 10) + 1);
const leadingZeroCount = (terminatingInt.length - incrementedInt.length);
if (leadingZeroCount >= 1) {
terminatingInt = Array(leadingZeroCount).fill("0").concat(incrementedInt).join('');
} else {
terminatingInt = incrementedInt;
}
} else {
terminatingInt = '1';
}
return `${ (openingPartial || '') }${ terminatingInt }`;
}
// Should return 'foo_003_bar1'.
console.log(incrementString("foo_003_bar"));
// Should return 'foo_003_bar_01'.
console.log(incrementString("foo_003_bar_00"));
// Should return 'foobar1'.
console.log(incrementString("foobar"));
// Should return 'foobar2'.
console.log(incrementString("foobar1"));
// Should return 'foobar100'.
console.log(incrementString("foobar099"));
// Should return 'foobar002'.
console.log(incrementString("foobar001"));
// Should return 'foobar001'.
console.log(incrementString("foobar000"));
// Should return 'foobar0101'.
console.log(incrementString("foobar0100"));
.as-console-wrapper { max-height: 100%!important; top: 0; }

Convert array to pipe separated

How to convert array EX-["lat","long","abc","def","abcc","deef",]
into [lat,long | abc,def | abcc,deef] in javascript.
I am facing issue with distance matrix Api...
Below is my code
export async function getStoreDistance(locationDetails) {
destinationRequest = [];
let destinationRequest = locationDetails.destinations.map(location => {
console.log('destreqlocation', location);
return `${location.lat},${location.long} `;
});
return await axios
.get(
`https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial
&origins=${locationDetails.origins.map(function(locationDetail) {
return locationDetail;
})}
&destinations=${destinationRequest}&key=**********`,
)
.then(function(response) {
// handle success
// return response;
})
.catch(function(error) {
// handle error
return error;
});
}
My solution to the problem.
const so1 = ["lat","long","abc","def","abcc","deef"]
let result = so1
.map((item, id, array) => ((id % 2) !== 0 && id !== (array.length - 1)) ? item + '|' : (id !== (array.length - 1)) ? item + '&' : item)
.join('')
.replace(/&/g, ',')
console.log( result )
console.log( `[${result}]` )
Try something like below
input = ["lat", "long", "abc", "def", "abcc", "deef"];
const [lat, long, ...rest] = input;
res = rest.reduce((acc, val, index) => {
if(index % 2 === 0) acc.push([]);
acc[acc.length -1].push(val);
return acc;
}, []);
resFinal = [[lat, long], ...res];
console.log(resFinal);
resFinalStr = resFinal.reduce((acc, val, index)=> {
if(index !== resFinal.length -1){
acc+=(val.join(",")) + "|";
}else{
acc += val.join(",")
}
return acc;
}, "")
console.log(resFinalStr)
console.log(`[${resFinalStr}]`)
An old-fashioned for loop should do the job fine:
function getStoreDistance(locationDetails) {
destinationRequest = locationDetails[0] || "";
for (let i = 1; i < locationDetails.length; i++) {
destinationRequest += (i % 2 ? "," : " | ") + locationDetails[i];
}
return destinationRequest;
}
// Demo
let response = ["lat","long","abc","def","abcc","deef"];
console.log(getStoreDistance(response));

custom rules parser

I have a set of masks.
The masks look like this
'09{2,9}n(6)'
//read as 09
//[a number between 2 and 9]
//[a random number][repeat expression 6 times]
'029n(7,10)'
//read as 029
//[a random number][repeat expression between 7 and 10 times]
'029n(2,5){8,15}(7,10)n'
//read as 029
//[a random number][repeat expression between 2 and 5 times]
//[a random number between 8 and 15][repeat expression between 7 and 10 times]
//[a random number]
as an example expession 3 would work out as
'029n(4){4,9}(7)n'
'029nnnn{4,9}{4,9}{4,9}{4,9}{4,9}{4,9}{4,9}n
'029nnnn{5}{9}{4}{8}{5}{9}{9}n
'029nnnn5948599n'
'029023559485999'
I need to write a parser in javascript that can generate a string based on those rules.
Note that this is not validation, it is string generation.
Whats the best way to do this?
Trying out a custom parser. Use as,
var generator = new PatternGenerator('09{2,9}n(6)');
generator.generate(); // 096555555
generator.generate(); // 095000000
Checkout this example.
And the constructor function,
function PatternGenerator(pattern) {
var tokens = null;
this.generate = function() {
var stack = [];
tokens = pattern.split('');
// Read each token and add
while (tokens.length) {
var token = lookahead();
if (isDigit(token)) {
stack.push(consumeNumber());
}
else if (token == "n") {
stack.push(consumeVariableNumber());
}
else if (token == "(") {
var topObject = stack.pop();
stack.push(consumeRepetition(topObject));
}
else if (token == "{") {
stack.push(consumeVariableRangeNumber());
}
else {
throw new Error("Invalid input");
}
}
return stack.join('');
}
// [0-9]+
function consumeNumber() {
var number = "";
while (isDigit(lookahead())) {
number += consume();
}
return number;
}
// "n"
function VariableNumber() {
var number = generateRandomNumber();
this.toString = function() {
return Number(number);
};
}
function consumeVariableNumber() {
consume();
return new VariableNumber();
}
// {x, y}
function VariableRangeNumber(start, end) {
var number = generateRandomNumberBetween(start, end);
this.toString = function() {
return Number(number);
};
}
function consumeVariableRangeNumber() {
consume(); // {
var firstNumber = consumeNumber();
consume(); // ,
var secondNumber = consumeNumber();
consume(); // }
return new VariableRangeNumber(firstNumber, secondNumber);
}
// <expression>(x)
function Repeat(object, times) {
this.toString = function() {
var string = "";
for (var i = 0; i < times; i++) {
string += object;
}
return string;
};
}
// <expression>(x, y)
function RepeatWithRange(object, start, end) {
var times = generateRandomNumberBetween(start, end);
this.toString = function() {
return new Repeat(object, times).toString();
};
}
function consumeRepetition(object) {
consume(); // (
var firstNumber, secondNumber;
var firstNumber = consumeNumber();
if (lookahead() == ",") {
consume(); // ,
secondNumber = consumeNumber();
}
consume(); // )
if (typeof secondNumber == 'undefined') {
return new Repeat(objectToRepeat, firstNumber);
}
else {
return new RepeatWithRange(object, firstNumber, secondNumber);
}
}
// Helpers to generate random integers
function generateRandomNumber() {
var MAX = Math.pow(2, 52);
return generateRandomNumberBetween(0, MAX);
}
function generateRandomNumberBetween(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function lookahead() {
return tokens[0];
}
function consume() {
return tokens.shift();
}
function isDigit(character) {
return /\d/.test(character);
}
}

Categories

Resources