Regex sometimes returning null in Javascript - javascript

While using regex to match values inside of a formula string I found this issue that regex would return null even though there is a match.
var formula = "round((DATAtotal_sq_ft * .6) + (QTY16 * 4) + (QTY17 * 2) + QTY18 + QTY15 + QTY12 * 18 / 3000, 1)";
const qtyRegex = /(QTY)(\d*)|(LEN)(\d*)|(DATA)([a-zA-Z_-|\d]*)/gm;
let m;
while ((m = qtyRegex.exec(formula)) !== null) {
var val = 0; // Here is irrelevant code that gets the value
formula = formula.replace(m[0], val);
}
console.log(formula);
In the above snippet you can see the result that a couple of the values don't get replaced but at the same time all of them get detected by
regex101 https://regex101.com/r/WTpvFq/1. For some reason I cant seem to narrow down what I'm doing wrong even after reading a number of different answers to similar problems.
I could use a workaround and use formula.match(qtyRegex) but I am really certain it's just an error in the Regex so I'd prefer properly fixing it instead of plastering it with a patch.

Instead of a while loop you can directly replace using the RegExp with the replacer callback of String.replace like this:
formula = formula.replace(qtyRegex, replacer)
// take a look at the docs I've linked above for an explanation of these params
function replacer(match, p1, p2, ..., offset, string) {
let calculatedValue = 0 // perform your irrelevant code that gets the value here
return calculatedValue
}

When you use the g modifier in a regexp, it remembers the position in the string where it last matched, and subsequent matches start from that position. But your code is replacing the match with a shorter string, so the remembered position may be past the beginning of the next match, and it doesn't find it.
Get rid of the g modifier and it will search from the beginning each time.
var formula = "round((DATAtotal_sq_ft * .6) + (QTY16 * 4) + (QTY17 * 2) + QTY18 + QTY15 + QTY12 * 18 / 3000, 1)";
const qtyRegex = /(QTY)(\d*)|(LEN)(\d*)|(DATA)([a-zA-Z_-|\d]*)/m;
let m;
while ((m = qtyRegex.exec(formula)) !== null) {
var val = 0; // Here is irrelevant code that gets the value
formula = formula.replace(m[0], val);
}
console.log(formula);

Related

Find line by character position

I have a string of around 4MB (4 million characters) and around 30.000 lines in a variable. Next I have the index of a character, lets say 3605506, what would be the quickest most efficient way to find on which line this character is? I need to do this hundreds of times after each other, so that's why it's relatively important it's efficient.
Pass the string and and index to the below function. It splits the string based on new line characters and checks if the count has passed the index value.
function getlineNumberofChar(data,index) {
var perLine = data.split('\n');
var total_length = 0;
for (i = 0; i < perLine.length; i++) {
total_length += perLine[i].length;
if (total_length >= index)
return i + 1;
}
}
Similar to brute_force but with the off-by-1 error fixed. Also returns column number.
const lines = code.split('\n')
function findLineColForByte(lines, index) {
let totalLength = 0
let lineStartPos = 0
for (let lineNo = 0; lineNo < lines.length; lineNo++) {
totalLength += lines[lineNo].length + 1 // Because we removed the '\n' during split.
if (index < totalLength) {
const colNo = index - lineStartPos
return [lineNo + 1, colNo]
}
lineStartPos = totalLength
}
}
You mentioned that
I need to do this hundreds of times after each other, so that's why it's relatively important it's efficient.
Most of these solutions require the computations to be done for each lookup, which means you are doing a lot of work over and over again.
To checkpoint some of these computations would (could) improve efficiency greatly.
Of course, first things first we need to split the lines up:
/**
* Returns a tuple (array with two elements) containing the split lines
* and whether or not the last character was a newLine
*
* #param {string} stringData The string to split
*
* #return {array} a tuple containing the lines
* and a boolean for if the last line has a newLine
*/
function splitLines( stringData ) {
var lines = stringData.split("\n");
if(stringData.slice(-1) === '\n') {
lines.pop(); // Remove last empty line
return [lines, true];
} else {
return [lines, false];
}
}
This will ensure that our last line is not an empty string, if this is arbitrary, you don't need to check for this.
Next up is computing the cumulative character count for each line, that is, after line x there have been n total characters.
/**
* Returns an array with the cumulative character count from the beginning,
* based on the line number
*
* #param {array} lineData The lines of the string
* #param {boolean} lastLineHasNewLineChar Whether or not the last line had a newLineChar
*
* #return {array} The cumulative character counts for each line
* (e.g.) Line 0 has 18 chars plus a newLine, or 19; Line 1 has 8 chars, so 28, etc, etc.
*/
function buildLineEndingPositions( lineData, lastLineHasNewLineChar = false ) {
var cumulativeSum = (sum => lineCharCount => sum += lineCharCount)(0); // Start sum at 0, keep adding the chars from each line.
var numLines = lineData.length;
var lineLengths = lineData.map( (line, index) => {
if(numLines - 1 === index && !lastLineHasNewLineChar) {
return line.length; // last line, last char was not a new line
} else {
return line.length + 1; // new line char was stripped
}
});
return lineLengths.map(cumulativeSum);
}
Finally, we can compute these once, and access them for any number of future lookups based on character position to determine the line (the first index to be less than or equal to the cumulative character count)
const testString = "There once was a guy from france\nHe really liked to dance\nUntil one day, his legs ran away\nIdk where I was going with this";
const [testLines, lastLineHadNewLineChar] = splitLines(testString);
const cumulativeCharCounts = buildLineEndingPositions(testLines, lastLineHadNewLineChar);
console.log(cumulativeCharCounts); //[33, 58, 91, 122]
By iterating through the cumulativeCharCounts we can now use the index to determine the line number with a simple boolean compare to the desired char position, until we reach the first cumulative position that is less than or equal to our desired position. The split and cumulative counts are figured out 1x and reused, thus less overhead for each of the hundreds of calls.
// Let this be your 4MB string.
var str = "This \n is a\n test\n string."
// Let this be the index of the character you are finding within the 4MB string.
var index = str.indexOf("test")
// Create substring from beginning to index of character.
var substr = str.substring(0, index)
// Count the number of new lines.
var numberOfLines = (function(){
try{
// Add 1 to final result to account for the first line.
return substr.match(new RegExp("\n", "g")).length + 1
} catch(e){
// Return 1 if none found because the character is found on the first line.
return 1
}})()

JS regex match returning null

I'm trying to do a coding challenge on Coderbyte. I have to find the difference in minutes between two inputted times (eg: "12:00am-12:00pm"). This is my code:
function getMinutes(str) {
var pattern = /(\d+)\:(\d+)([ap]m)/i;
var matches = str.toString().match(pattern);
**// return matches**
if (matches == null) {
return matches;
}
var hour = parseInt(matches[1]);
var minutes = parseInt(matches[2]);
var extra = (matches[3] == "am") ? 0 : 720;
if (hour == 12)
hour = 0;
return (hour * 60) + minutes + extra;
}
function CountingMinutesI(str) {
var chunks = str.split("-");
var minuteA = getMinutes(chunks[0]), minuteB = getMinutes(chunks[1]);
return getMinutes(minuteA) + " " + getMinutes(minuteB);
}
// keep this function call here
// to see how to enter arguments in JavaScript scroll down
CountingMinutesI(readline());
For some reason in getMinutes, matches is null even though it shouldn't be. If you uncomment the bolded line that says "return matches", then it will give me the valid array with all the matches. But if I comment that line out, then matches becomes null. Why? This is so strange.
There is simple oversight in CountingMinutesI(). You are going getMinutes twice. Replace
return getMinutes(minuteA) + " " + getMinutes(minuteB);
With
return minuteA + " " + minuteB;
In the CountingMinutesI function, you are calling getMinutes() a total of 4 times, one for the first chunk, one for the second chunk, one with the result of the first call (0), and one with the result of the second call (720).
Those second two calls are the problem, they result in the function trying to match the regex against "0" and "720" respectively, neither of which will work.

Javascript rounding failure

This is the rounding function we are using (which is taken from stackoverflow answers on how to round). It rounds half up to 2dp (by default)
e.g. 2.185 should go to 2.19
function myRound(num, places) {
if (places== undefined) {
// default to 2dp
return Math.round(num* 100) / 100;
}
var mult = Math.pow(10,places);
return Math.round(num* mult) / mult;
}
It has worked well but now we have found some errors in it (in both chrome and running as jscript classic asp on IIS 7.5).
E.g.:
alert(myRound(2.185)); // = 2.19
alert (myRound(122.185)); // = 122.19
alert (myRound(511.185)); // = 511.19
alert (myRound(522.185)); // = 522.18 FAIL!!!!
alert (myRound(625.185)); // = 625.18 FAIL!!!!
Does anyone know:
Why this happens.
How we can round half up to 2 dp without random rounding errors like this.
update: OK, the crux of the problem is that in js, 625.185 * 100 = 62518.499999
How can we get over this?
Your problem is not easily resolved. It occurs because IEEE doubles use a binary representation that cannot exactly represent all decimals. The closest internal representation to 625.185 is 625.18499999999994543031789362430572509765625, which is ever so slightly less than 625.185, and for which the correct rounding is downwards.
Depending on your circumstances, you might get away with the following:
Math.round(Math.round(625.185 * 1000) / 10) / 100 // evaluates to 625.19
This isn't strictly correct, however, since, e.g., it will round, 625.1847 upwards to 625.19. Only use it if you know that the input will never have more than three decimal places.
A simpler option is to add a small epsilon before rounding:
Math.round(625.185 * 100 + 1e-6) / 100
This is still a compromise, since you might conceivably have a number that is very slightly less than 625.185, but it's probably more robust than the first solution. Watch out for negative numbers, though.
Try using toFixed function on value.
example is below:
var value = parseFloat(2.185);
var fixed = value.toFixed(2);
alert(fixed);
I tried and it worked well.
EDIT: You can always transform string to number using parseFloat(stringVar).
EDIT2:
function myRound(num, places) {
return parseFloat(num.toFixed(places));
}
EDIT 3:
Updated answer, tested and working:
function myRound(num, places) {
if (places== undefined) {
places = 2;
}
var mult = Math.pow(10,places + 1);
var mult2 = Math.pow(10,places);
return Math.round(num* mult / 10) / mult2;
}
EDIT 4:
Tested on most examples noted in comments:
function myRound(num, places) {
if (places== undefined) {
places = 2;
}
var mult = Math.pow(10,places);
var val = num* mult;
var intVal = parseInt(val);
var floatVal = parseFloat(val);
if (intVal < floatVal) {
val += 0.1;
}
return Math.round(val) / mult;
}
EDIT 5:
Only solution that I managed to find is to use strings to get round on exact decimal.
Solution is pasted below, with String prototype extension method, replaceAt.
Please check and let me know if anyone finds some example that is not working.
function myRound2(num, places) {
var retVal = null;
if (places == undefined) {
places = 2;
}
var splits = num.split('.');
if (splits && splits.length <= 2) {
var wholePart = splits[0];
var decimalPart = null;
if (splits.length > 1) {
decimalPart = splits[1];
}
if (decimalPart && decimalPart.length > places) {
var roundingDigit = parseInt(decimalPart[places]);
var previousDigit = parseInt(decimalPart[places - 1]);
var increment = (roundingDigit < 5) ? 0 : 1;
previousDigit = previousDigit + increment;
decimalPart = decimalPart.replaceAt(places - 1, previousDigit + '').substr(0, places);
}
retVal = parseFloat(wholePart + '.' + decimalPart);
}
return retVal;
}
String.prototype.replaceAt = function (index, character) {
return this.substr(0, index) + character + this.substr(index + character.length);
}
OK, found a "complete" solution to the issue.
Firstly, donwnloaded Big.js from here: https://github.com/MikeMcl/big.js/
Then modified the source so it would work with jscript/asp:
/* big.js v2.1.0 https://github.com/MikeMcl/big.js/LICENCE */
var Big = (function ( global ) {
'use strict';
:
// EXPORT
return Big;
})( this );
Then did my calculation using Big types and used the Big toFixed(dp), then converted back into a number thusly:
var bigMult = new Big (multiplier);
var bigLineStake = new Big(lineStake);
var bigWin = bigLineStake.times(bigMult);
var strWin = bigWin.toFixed(2); // this does the rounding correctly.
var win = parseFloat(strWin); // back to a number!
This basically uses Bigs own rounding in its toFixed, which seems to work correctly in all cases.
Shame Big doesnt have a method to convert back to a number without having to go through a string.

Working with hex strings and hex values more easily in Javascript

I have some code which takes strings representing hexadecimal numbers - hex colors, actually - and adds them. For example, adding aaaaaa and 010101 gives the output ababab.
However, my method seems unnecessarily long and complicated:
var hexValue = "aaaaaa";
hexValue = "0x" + hexValue;
hexValue = parseInt(hexValue, 16);
hexValue = hexValue + 0x010101;
hexValue = hexValue.toString(16);
document.write(hexValue); // outputs 'ababab'
The hex value is still a string after concatenating 0x, so then I have to change it to a number, then I can add, and then I have to change it back into hex format! There are even more steps if the number I'm adding to it is a hexadecimal string to begin with, or if you take into consideration that I am removing the # from the hex color before all this starts.
Surely there's a simpler way to do such simple hexadecimal calculations! And just to be clear, I don't mean just putting it all on one line like (parseInt("0x"+"aaaaaa",16)+0x010101).toString(16) or using shorthand - I mean actually doing less operations.
Is there some way to get javascript to stop using decimal for all of its mathematical operations and use hex instead? Or is there some other method of making JS work with hex more easily?
No, there is no way to tell the JavaScript language to use hex integer format instead of decimal by default. Your code is about as concise as it gets but note that you do not need to prepend the "0x" base indicator when you use "parseInt" with a base.
Here is how I would approach your problem:
function addHexColor(c1, c2) {
var hexStr = (parseInt(c1, 16) + parseInt(c2, 16)).toString(16);
while (hexStr.length < 6) { hexStr = '0' + hexStr; } // Zero pad.
return hexStr;
}
addHexColor('aaaaaa', '010101'); // => 'ababab'
addHexColor('010101', '010101'); // => '020202'
As mentioned by a commenter, the above solution is chock full of problems, so below is a function that does proper input validation and adds color channels separately while checking for overflow.
function addHexColor2(c1, c2) {
const octetsRegex = /^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i
const m1 = c1.match(octetsRegex)
const m2 = c2.match(octetsRegex)
if (!m1 || !m2) {
throw new Error(`invalid hex color triplet(s): ${c1} / ${c2}`)
}
return [1, 2, 3].map(i => {
const sum = parseInt(m1[i], 16) + parseInt(m2[i], 16)
if (sum > 0xff) {
throw new Error(`octet ${i} overflow: ${m1[i]}+${m2[i]}=${sum.toString(16)}`)
}
return sum.toString(16).padStart(2, '0')
}).join('')
}
addHexColor2('aaaaaa', 'bogus!') // => Error: invalid hex color triplet(s): aaaaaa / bogus!
addHexColor2('aaaaaa', '606060') // => Error: octet 1 overflow: aa+60=10a
How about this:
var hexValue = "aaaaaa";
hexValue = (parseInt(hexValue, 16) + 0x010101).toString(16);
document.writeln(hexValue); // outputs 'ababab'
There is no need to add the 0x prefix if you use parseInt.
I think accepted answer is wrong. Hexadecimal color representation is not a linear. But instead, 3 sets of two characters are given to R, G & B.
So you can't just add a whole number and expect to RGB to add up correctly.
For Example
n1 = '005500'; <--- green
n2 = '00ff00'; <--- brighter green
Adding these numbers should result in a greener green.
In no way, adding greens should increase RED to increase. but by doing what accepted answer is doing, as in just treat whole number as one number then you'd carry over for numbers adding upto greater than f, f+1 = 10.
you get `015400` so by adding greens the RED increased .... WRONG
adding 005500 + 00ff00 should result in, = 00ff00. You can't add more green to max green.
For folks looking for a function that can add and subtract HEX colors without going out of bounds on an individual tuple, I wrote this function a few minutes ago to do just that:
export function shiftColor(base, change, direction) {
const colorRegEx = /^\#?[A-Fa-f0-9]{6}$/;
// Missing parameter(s)
if (!base || !change) {
return '000000';
}
// Invalid parameter(s)
if (!base.match(colorRegEx) || !change.match(colorRegEx)) {
return '000000';
}
// Remove any '#'s
base = base.replace(/\#/g, '');
change = change.replace(/\#/g, '');
// Build new color
let newColor = '';
for (let i = 0; i < 3; i++) {
const basePiece = parseInt(base.substring(i * 2, i * 2 + 2), 16);
const changePiece = parseInt(change.substring(i * 2, i * 2 + 2), 16);
let newPiece = '';
if (direction === 'add') {
newPiece = (basePiece + changePiece);
newPiece = newPiece > 255 ? 255 : newPiece;
}
if (direction === 'sub') {
newPiece = (basePiece - changePiece);
newPiece = newPiece < 0 ? 0 : newPiece;
}
newPiece = newPiece.toString(16);
newPiece = newPiece.length < 2 ? '0' + newPiece : newPiece;
newColor += newPiece;
}
return newColor;
}
You pass your base color as parameter 1, your change as parameter 2, and then 'add' or 'sub' as the last parameter depending on your intent.

How can I pad a value with leading zeros?

What is the recommended way to zerofill a value in JavaScript? I imagine I could build a custom function to pad zeros on to a typecasted value, but I'm wondering if there is a more direct way to do this?
Note: By "zerofilled" I mean it in the database sense of the word (where a 6-digit zerofilled representation of the number 5 would be "000005").
I can't believe all the complex answers on here... Just use this:
var zerofilled = ('0000'+n).slice(-4);
let n = 1
var zerofilled = ('0000'+n).slice(-4);
console.log(zerofilled)
Simple way. You could add string multiplication for the pad and turn it into a function.
var pad = "000000";
var n = '5';
var result = (pad+n).slice(-pad.length);
As a function,
function paddy(num, padlen, padchar) {
var pad_char = typeof padchar !== 'undefined' ? padchar : '0';
var pad = new Array(1 + padlen).join(pad_char);
return (pad + num).slice(-pad.length);
}
var fu = paddy(14, 5); // 00014
var bar = paddy(2, 4, '#'); // ###2
Since ECMAScript 2017 we have padStart:
const padded = (.1 + "").padStart(6, "0");
console.log(`-${padded}`);
Before ECMAScript 2017
With toLocaleString:
var n=-0.1;
var res = n.toLocaleString('en', {minimumIntegerDigits:4,minimumFractionDigits:2,useGrouping:false});
console.log(res);
I actually had to come up with something like this recently.
I figured there had to be a way to do it without using loops.
This is what I came up with.
function zeroPad(num, numZeros) {
var n = Math.abs(num);
var zeros = Math.max(0, numZeros - Math.floor(n).toString().length );
var zeroString = Math.pow(10,zeros).toString().substr(1);
if( num < 0 ) {
zeroString = '-' + zeroString;
}
return zeroString+n;
}
Then just use it providing a number to zero pad:
> zeroPad(50,4);
"0050"
If the number is larger than the padding, the number will expand beyond the padding:
> zeroPad(51234, 3);
"51234"
Decimals are fine too!
> zeroPad(51.1234, 4);
"0051.1234"
If you don't mind polluting the global namespace you can add it to Number directly:
Number.prototype.leftZeroPad = function(numZeros) {
var n = Math.abs(this);
var zeros = Math.max(0, numZeros - Math.floor(n).toString().length );
var zeroString = Math.pow(10,zeros).toString().substr(1);
if( this < 0 ) {
zeroString = '-' + zeroString;
}
return zeroString+n;
}
And if you'd rather have decimals take up space in the padding:
Number.prototype.leftZeroPad = function(numZeros) {
var n = Math.abs(this);
var zeros = Math.max(0, numZeros - n.toString().length );
var zeroString = Math.pow(10,zeros).toString().substr(1);
if( this < 0 ) {
zeroString = '-' + zeroString;
}
return zeroString+n;
}
Cheers!
XDR came up with a logarithmic variation that seems to perform better.
WARNING: This function fails if num equals zero (e.g. zeropad(0, 2))
function zeroPad (num, numZeros) {
var an = Math.abs (num);
var digitCount = 1 + Math.floor (Math.log (an) / Math.LN10);
if (digitCount >= numZeros) {
return num;
}
var zeroString = Math.pow (10, numZeros - digitCount).toString ().substr (1);
return num < 0 ? '-' + zeroString + an : zeroString + an;
}
Speaking of performance, tomsmeding compared the top 3 answers (4 with the log variation). Guess which one majorly outperformed the other two? :)
Modern browsers now support padStart, you can simply now do:
string.padStart(maxLength, "0");
Example:
string = "14";
maxLength = 5; // maxLength is the max string length, not max # of fills
res = string.padStart(maxLength, "0");
console.log(res); // prints "00014"
number = 14;
maxLength = 5; // maxLength is the max string length, not max # of fills
res = number.toString().padStart(maxLength, "0");
console.log(res); // prints "00014"
Here's what I used to pad a number up to 7 characters.
("0000000" + number).slice(-7)
This approach will probably suffice for most people.
Edit: If you want to make it more generic you can do this:
("0".repeat(padding) + number).slice(-padding)
Edit 2: Note that since ES2017 you can use String.prototype.padStart:
number.toString().padStart(padding, "0")
Unfortunately, there are a lot of needless complicated suggestions for this problem, typically involving writing your own function to do math or string manipulation or calling a third-party utility. However, there is a standard way of doing this in the base JavaScript library with just one line of code. It might be worth wrapping this one line of code in a function to avoid having to specify parameters that you never want to change like the local name or style.
var amount = 5;
var text = amount.toLocaleString('en-US',
{
style: 'decimal',
minimumIntegerDigits: 3,
useGrouping: false
});
This will produce the value of "005" for text. You can also use the toLocaleString function of Number to pad zeros to the right side of the decimal point.
var amount = 5;
var text = amount.toLocaleString('en-US',
{
style: 'decimal',
minimumFractionDigits: 2,
useGrouping: false
});
This will produce the value of "5.00" for text. Change useGrouping to true to use comma separators for thousands.
Note that using toLocaleString() with locales and options arguments is standardized separately in ECMA-402, not in ECMAScript. As of today, some browsers only implement basic support, i.e. toLocaleString() may ignore any arguments.
Complete Example
If the fill number is known in advance not to exceed a certain value, there's another way to do this with no loops:
var fillZeroes = "00000000000000000000"; // max number of zero fill ever asked for in global
function zeroFill(number, width) {
// make sure it's a string
var input = number + "";
var prefix = "";
if (input.charAt(0) === '-') {
prefix = "-";
input = input.slice(1);
--width;
}
var fillAmt = Math.max(width - input.length, 0);
return prefix + fillZeroes.slice(0, fillAmt) + input;
}
Test cases here: http://jsfiddle.net/jfriend00/N87mZ/
The quick and dirty way:
y = (new Array(count + 1 - x.toString().length)).join('0') + x;
For x = 5 and count = 6 you'll have y = "000005"
Here's a quick function I came up with to do the job. If anyone has a simpler approach, feel free to share!
function zerofill(number, length) {
// Setup
var result = number.toString();
var pad = length - result.length;
while(pad > 0) {
result = '0' + result;
pad--;
}
return result;
}
ECMAScript 2017:
use padStart or padEnd
'abc'.padStart(10); // " abc"
'abc'.padStart(10, "foo"); // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
More info:
https://github.com/tc39/proposal-string-pad-start-end
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
I often use this construct for doing ad-hoc padding of some value n, known to be a positive, decimal:
(offset + n + '').substr(1);
Where offset is 10^^digits.
E.g., padding to 5 digits, where n = 123:
(1e5 + 123 + '').substr(1); // => 00123
The hexadecimal version of this is slightly more verbose:
(0x100000 + 0x123).toString(16).substr(1); // => 00123
Note 1: I like #profitehlolz's solution as well, which is the string version of this, using slice()'s nifty negative-index feature.
I really don't know why, but no one did it in the most obvious way. Here it's my implementation.
Function:
/** Pad a number with 0 on the left */
function zeroPad(number, digits) {
var num = number+"";
while(num.length < digits){
num='0'+num;
}
return num;
}
Prototype:
Number.prototype.zeroPad=function(digits){
var num=this+"";
while(num.length < digits){
num='0'+num;
}
return(num);
};
Very straightforward, I can't see any way how this can be any simpler. For some reason I've seem many times here on SO, people just try to avoid 'for' and 'while' loops at any cost. Using regex will probably cost way more cycles for such a trivial 8 digit padding.
In all modern browsers you can use
numberStr.padStart(numberLength, "0");
function zeroFill(num, numLength) {
var numberStr = num.toString();
return numberStr.padStart(numLength, "0");
}
var numbers = [0, 1, 12, 123, 1234, 12345];
numbers.forEach(
function(num) {
var numString = num.toString();
var paddedNum = zeroFill(numString, 5);
console.log(paddedNum);
}
);
Here is the MDN reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
I use this snippet to get a five-digits representation:
(value+100000).toString().slice(-5) // "00123" with value=123
The power of Math!
x = integer to pad
y = number of zeroes to pad
function zeroPad(x, y)
{
y = Math.max(y-1,0);
var n = (x / Math.pow(10,y)).toFixed(y);
return n.replace('.','');
}
This is the ES6 solution.
function pad(num, len) {
return '0'.repeat(len - num.toString().length) + num;
}
alert(pad(1234,6));
Not that this question needs more answers, but I thought I would add the simple lodash version of this.
_.padLeft(number, 6, '0')
I didn't see anyone point out the fact that when you use String.prototype.substr() with a negative number it counts from the right.
A one liner solution to the OP's question, a 6-digit zerofilled representation of the number 5, is:
console.log(("00000000" + 5).substr(-6));
Generalizing we'll get:
function pad(num, len) { return ("00000000" + num).substr(-len) };
console.log(pad(5, 6));
console.log(pad(45, 6));
console.log(pad(345, 6));
console.log(pad(2345, 6));
console.log(pad(12345, 6));
Don't reinvent the wheel; use underscore string:
jsFiddle
var numToPad = '5';
alert(_.str.pad(numToPad, 6, '0')); // Yields: '000005'
After a, long, long time of testing 15 different functions/methods found in this questions answers, I now know which is the best (the most versatile and quickest).
I took 15 functions/methods from the answers to this question and made a script to measure the time taken to execute 100 pads. Each pad would pad the number 9 with 2000 zeros. This may seem excessive, and it is, but it gives you a good idea about the scaling of the functions.
The code I used can be found here:
https://gist.github.com/NextToNothing/6325915
Feel free to modify and test the code yourself.
In order to get the most versatile method, you have to use a loop. This is because with very large numbers others are likely to fail, whereas, this will succeed.
So, which loop to use? Well, that would be a while loop. A for loop is still fast, but a while loop is just slightly quicker(a couple of ms) - and cleaner.
Answers like those by Wilco, Aleksandar Toplek or Vitim.us will do the job perfectly.
Personally, I tried a different approach. I tried to use a recursive function to pad the string/number. It worked out better than methods joining an array but, still, didn't work as quick as a for loop.
My function is:
function pad(str, max, padder) {
padder = typeof padder === "undefined" ? "0" : padder;
return str.toString().length < max ? pad(padder.toString() + str, max, padder) : str;
}
You can use my function with, or without, setting the padding variable. So like this:
pad(1, 3); // Returns '001'
// - Or -
pad(1, 3, "x"); // Returns 'xx1'
Personally, after my tests, I would use a method with a while loop, like Aleksandar Toplek or Vitim.us. However, I would modify it slightly so that you are able to set the padding string.
So, I would use this code:
function padLeft(str, len, pad) {
pad = typeof pad === "undefined" ? "0" : pad + "";
str = str + "";
while(str.length < len) {
str = pad + str;
}
return str;
}
// Usage
padLeft(1, 3); // Returns '001'
// - Or -
padLeft(1, 3, "x"); // Returns 'xx1'
You could also use it as a prototype function, by using this code:
Number.prototype.padLeft = function(len, pad) {
pad = typeof pad === "undefined" ? "0" : pad + "";
var str = this + "";
while(str.length < len) {
str = pad + str;
}
return str;
}
// Usage
var num = 1;
num.padLeft(3); // Returns '001'
// - Or -
num.padLeft(3, "x"); // Returns 'xx1'
First parameter is any real number, second parameter is a positive integer specifying the minimum number of digits to the left of the decimal point and third parameter is an optional positive integer specifying the number if digits to the right of the decimal point.
function zPad(n, l, r){
return(a=String(n).match(/(^-?)(\d*)\.?(\d*)/))?a[1]+(Array(l).join(0)+a[2]).slice(-Math.max(l,a[2].length))+('undefined'!==typeof r?(0<r?'.':'')+(a[3]+Array(r+1).join(0)).slice(0,r):a[3]?'.'+a[3]:''):0
}
so
zPad(6, 2) === '06'
zPad(-6, 2) === '-06'
zPad(600.2, 2) === '600.2'
zPad(-600, 2) === '-600'
zPad(6.2, 3) === '006.2'
zPad(-6.2, 3) === '-006.2'
zPad(6.2, 3, 0) === '006'
zPad(6, 2, 3) === '06.000'
zPad(600.2, 2, 3) === '600.200'
zPad(-600.1499, 2, 3) === '-600.149'
The latest way to do this is much simpler:
var number = 2
number.toLocaleString(undefined, {minimumIntegerDigits:2})
output: "02"
Just another solution, but I think it's more legible.
function zeroFill(text, size)
{
while (text.length < size){
text = "0" + text;
}
return text;
}
This one is less native, but may be the fastest...
zeroPad = function (num, count) {
var pad = (num + '').length - count;
while(--pad > -1) {
num = '0' + num;
}
return num;
};
My solution
Number.prototype.PadLeft = function (length, digit) {
var str = '' + this;
while (str.length < length) {
str = (digit || '0') + str;
}
return str;
};
Usage
var a = 567.25;
a.PadLeft(10); // 0000567.25
var b = 567.25;
b.PadLeft(20, '2'); // 22222222222222567.25
With ES6+ JavaScript:
You can "zerofill a number" with something like the following function:
/**
* #param number The number
* #param minLength Minimal length for your string with leading zeroes
* #return Your formatted string
*/
function zerofill(nb, minLength) {
// Convert your number to string.
let nb2Str = nb.toString()
// Guess the number of zeroes you will have to write.
let nbZeroes = Math.max(0, minLength - nb2Str.length)
// Compute your result.
return `${ '0'.repeat(nbZeroes) }${ nb2Str }`
}
console.log(zerofill(5, 6)) // Displays "000005"
With ES2017+:
/**
* #param number The number
* #param minLength Minimal length for your string with leading zeroes
* #return Your formatted string
*/
const zerofill = (nb, minLength) => nb.toString().padStart(minLength, '0')
console.log(zerofill(5, 6)) // Displays "000005"
Use recursion:
function padZero(s, n) {
s = s.toString(); // In case someone passes a number
return s.length >= n ? s : padZero('0' + s, n);
}
Some monkeypatching also works
String.prototype.padLeft = function (n, c) {
if (isNaN(n))
return null;
c = c || "0";
return (new Array(n).join(c).substring(0, this.length-n)) + this;
};
var paddedValue = "123".padLeft(6); // returns "000123"
var otherPadded = "TEXT".padLeft(8, " "); // returns " TEXT"
function pad(toPad, padChar, length){
return (String(toPad).length < length)
? new Array(length - String(toPad).length + 1).join(padChar) + String(toPad)
: toPad;
}
pad(5, 0, 6) = 000005
pad('10', 0, 2) = 10 // don't pad if not necessary
pad('S', 'O', 2) = SO
...etc.
Cheers

Categories

Resources