Related
I need to create some logic that allows for validation of a specified IP range. Not a single IP address but two addresses that make up a single range.
I thought this would be somewhat simple so I devised some JS code that splits up the two input strings represented the ranges using a . delimiter and then compared each number in the first range with the equivalent number in the second range. If the start number is greater than the end number then the range would be invalid.
This works, to a degree. However it's not completely accurate as I'm getting pairs such as 127.0.0.3 / 128.0.0.1 which return false when in reality this pair of IP addresses is a valid range (ignoring the technicalities with using 127 etc.)
I'm not sure how exactly to check for a valid IP range, using Google doesn't seem to return any information on how to validate an IP range either.
How can I change my code around so that all invalid ranges are included and all invalid are excluded?
getIpRangeValidStates() : boolean[] {
console.log("getIdRangeValidStates");
let validStates = [];
for(let i = 0; i < this.ipRangeFormArray.length; i++){
let currentFormGroup = this.ipRangeFormArray.controls[i];
let startRangeElements = (currentFormGroup.get('startRange').value as string).split(".");
let endRangeElements = (currentFormGroup.get('endRange').value as string).split(".");
let rangeValid = true;
for(let j = 0; j < 4; j++) {
let startRangeAsInt = parseInt(startRangeElements[j]);
let endRangeAsInt = parseInt(endRangeElements[j]);
console.log(startRangeAsInt, " : ", endRangeAsInt);
if(isNaN(startRangeAsInt) || isNaN(endRangeAsInt))
{
console.log("NaN, invalid");
rangeValid = false;
}
else
{
if(startRangeAsInt > endRangeAsInt) {
console.log(startRangeAsInt, " > ", endRangeAsInt, "- invalid")
rangeValid = false;
}
}
}
rangeValid === false ? validStates.push(false) : validStates.push(true);
}
console.log("Range states: ", validStates);
return validStates;
}
I split your code in two loops: one that checks if the single elements are valid and one that validates the ranges.
Note that as soon as one of the elements in the first range is larger than its counterpart in the second range we can say that the range is invalid: there is no need to check further. Conversely, if the element in the first range is smaller that the element in the second range then the range is valid. There is really no need to check further unless the elements in both ranges are equal.
getIpRangeValidStates() : boolean[] {
console.log("getIdRangeValidStates");
let validStates = [];
for(let i = 0; i < this.ipRangeFormArray.length; i++){
let currentFormGroup = this.ipRangeFormArray.controls[i];
let startRangeElements = (currentFormGroup.get('startRange').value as string).split(".");
let endRangeElements = (currentFormGroup.get('endRange').value as string).split(".");
let rangeValid = true;
for(let j = 0; j < 4; j++) {
let startRangeAsInt = parseInt(startRangeElements[j]);
let endRangeAsInt = parseInt(endRangeElements[j]);
console.log(startRangeAsInt, " : ", endRangeAsInt);
if(isNaN(startRangeAsInt) || isNaN(endRangeAsInt))
{
console.log("NaN, invalid");
rangeValid = false;
break;
}
}
if (rangeValid) {
for(let j = 0; j < 4; j++) {
let startRangeAsInt = parseInt(startRangeElements[j]);
let endRangeAsInt = parseInt(endRangeElements[j]);
if(startRangeAsInt > endRangeAsInt) {
console.log(startRangeAsInt, " > ", endRangeAsInt, "- invalid");
rangeValid = false;
break;
}
if(startRangeAsInt < endRangeAsInt) {
console.log(startRangeAsInt, " < ", endRangeAsInt, "- valid");
break;
}
}
}
rangeValid === false ? validStates.push(false) : validStates.push(true);
}
console.log("Range states: ", validStates);
return validStates;
}
Of course, this can be simplified, but it will give you the idea.
When working with IPs i would recommend converting the IP to a number instead of using the string representation.
(An IP String is just a nice human-readable 4 byte integer, each block denoting the value of the byte at that position)
Once you have the ip in number form, checking if an ip comes after another is a simple <.
Also checking if an ip is within a certain range start, end is trivial:
ip >= start && ip <= end.
Code Example to convert the ip string into an integer and comparing it:
function ipToNumber(ipStr) {
let [a, b, c, d] = ipStr.split('.').map(Number);
// Note: Normally you would use shifts (a << 24), etc...
// but javascript only supports *signed* 32bit shifts, so we need to use this.
return (a * 2**24) + (b << 16) + (c << 8) + d;
}
let start = ipToNumber("127.0.0.1");
let end = ipToNumber("128.0.0.1");
console.log("Start: ", start.toString(16)); // 0x7f000001
console.log("End: ", end.toString(16)); // 0x80000001
console.log(start > end, start < end); // false, true
// check if ip is in range
let sample1 = ipToNumber("127.244.32.1");
console.log("sample1 in range:", sample1 >= start && sample1 <= end); // true
let sample2 = ipToNumber("128.1.0.22");
console.log("sample2 in range:", sample2 >= start && sample2 <= end); // false
Integrated in your code example it could look like this:
(Note: my typescript isn't very good, i converted it to javascript)
function toIpNumber(ipStr) {
let parts = ipStr.split('.').map(Number);
if(parts.length !== 4 || parts.some(e => isNaN(e) || e < 0 || e > 255)) return false;
let [a,b,c,d] = parts;
return (a * 2**24) + (b << 16) + (c << 8) + d;
}
function getIpRangeValidStates() {
console.log("getIdRangeValidStates");
let validStates = [];
for(let i = 0; i < this.ipRangeFormArray.length; i++){
let currentFormGroup = this.ipRangeFormArray.controls[i];
let startRange = toIpNumber(currentFormGroup.get('startRange').value);
let endRange = toIpNumber(currentFormGroup.get('endRange').value);
let rangeValid = true;
if(startRange === false || endRange === false)
validStates.push(false);
else
validStates.push(startRange <= endRange);
}
console.log("Range states: ", validStates);
return validStates;
}
If i Understood your problem.
The best way to check the provided ips are a range is by converting both ip's to a long int and compare the long int's.
Converting to int can be done like
const d = dottedDec.split('.');
const longInt = ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
Note: this code is not checking if its a valid ipv4 or not
Now when you have 2 numbers corresponding to 2 ips, you can compare it to check if its a range, also you can find how many ips in the range etc
I am in need of a JavaScript function which can take a value and pad it to a given length (I need spaces, but anything would do). I found this, but I have no idea what the heck it is doing and it doesn't seem to work for me.
String.prototype.pad = function(l, s, t) {
return s || (s = " "),
(l -= this.length) > 0 ?
(s = new Array(Math.ceil(l / s.length) + 1).join(s))
.substr(0, t = !t ? l : t == 1 ?
0 :
Math.ceil(l / 2)) + this + s.substr(0, l - t) :
this;
};
var s = "Jonas";
document.write(
'<h2>S = '.bold(), s, "</h2>",
'S.pad(20, "[]", 0) = '.bold(), s.pad(20, "[]", 0), "<br />",
'S.pad(20, "[====]", 1) = '.bold(), s.pad(20, "[====]", 1), "<br />",
'S.pad(20, "~", 2) = '.bold(), s.pad(20, "~", 2)
);
ECMAScript 2017 (ES8) added String.padStart (along with String.padEnd) for just this purpose:
"Jonas".padStart(10); // Default pad string is a space
"42".padStart(6, "0"); // Pad with "0"
"*".padStart(8, "-/|\\"); // produces '-/|\\-/|*'
If not present in the JavaScript host, String.padStart can be added as a polyfill.
Pre ES8
I found this solution here and this is for me much much simpler:
var n = 123
String("00000" + n).slice(-5); // returns 00123
("00000" + n).slice(-5); // returns 00123
(" " + n).slice(-5); // returns " 123" (with two spaces)
And here I made an extension to the string object:
String.prototype.paddingLeft = function (paddingValue) {
return String(paddingValue + this).slice(-paddingValue.length);
};
An example to use it:
function getFormattedTime(date) {
var hours = date.getHours();
var minutes = date.getMinutes();
hours = hours.toString().paddingLeft("00");
minutes = minutes.toString().paddingLeft("00");
return "{0}:{1}".format(hours, minutes);
};
String.prototype.format = function () {
var args = arguments;
return this.replace(/{(\d+)}/g, function (match, number) {
return typeof args[number] != 'undefined' ? args[number] : match;
});
};
This will return a time in the format "15:30".
A faster method
If you are doing this repeatedly, for example to pad values in an array, and performance is a factor, the following approach can give you nearly a 100x advantage in speed (jsPerf) over other solution that are currently discussed on the inter webs. The basic idea is that you are providing the pad function with a fully padded empty string to use as a buffer. The pad function just appends to string to be added to this pre-padded string (one string concat) and then slices or trims the result to the desired length.
function pad(pad, str, padLeft) {
if (typeof str === 'undefined')
return pad;
if (padLeft) {
return (pad + str).slice(-pad.length);
} else {
return (str + pad).substring(0, pad.length);
}
}
For example, to zero pad a number to a length of 10 digits,
pad('0000000000',123,true);
To pad a string with whitespace, so the entire string is 255 characters,
var padding = Array(256).join(' '), // make a string of 255 spaces
pad(padding,123,true);
Performance Test
See the jsPerf test here.
And this is faster than ES6 string.repeat by 2x as well, as shown by the revised JsPerf here
Please note that jsPerf is no longer online
Please note that the jsPerf site that we originally used to benchmark the various methods is no longer online. Unfortunately, this means we can't get to those test results. Sad but true.
String.prototype.padStart() and String.prototype.padEnd() are currently TC39 candidate proposals: see github.com/tc39/proposal-string-pad-start-end (only available in Firefox as of April 2016; a polyfill is available).
http://www.webtoolkit.info/javascript_pad.html
/**
*
* JavaScript string pad
* http://www.webtoolkit.info/
*
**/
var STR_PAD_LEFT = 1;
var STR_PAD_RIGHT = 2;
var STR_PAD_BOTH = 3;
function pad(str, len, pad, dir) {
if (typeof(len) == "undefined") { var len = 0; }
if (typeof(pad) == "undefined") { var pad = ' '; }
if (typeof(dir) == "undefined") { var dir = STR_PAD_RIGHT; }
if (len + 1 >= str.length) {
switch (dir){
case STR_PAD_LEFT:
str = Array(len + 1 - str.length).join(pad) + str;
break;
case STR_PAD_BOTH:
var padlen = len - str.length;
var right = Math.ceil( padlen / 2 );
var left = padlen - right;
str = Array(left+1).join(pad) + str + Array(right+1).join(pad);
break;
default:
str = str + Array(len + 1 - str.length).join(pad);
break;
} // switch
}
return str;
}
It's a lot more readable.
Here's a recursive approach to it.
function pad(width, string, padding) {
return (width <= string.length) ? string : pad(width, padding + string, padding)
}
An example...
pad(5, 'hi', '0')
=> "000hi"
ECMAScript 2017 adds a padStart method to the String prototype. This method will pad a string with spaces to a given length. This method also takes an optional string that will be used instead of spaces for padding.
'abc'.padStart(10); // " abc"
'abc'.padStart(10, "foo"); // "foofoofabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0"); // "00000abc"
'abc'.padStart(1); // "abc"
A padEnd method was also added that works in the same manner.
For browser compatibility (and a useful polyfill) see this link.
Using the ECMAScript 6 method String#repeat, a pad function is as simple as:
String.prototype.padLeft = function(char, length) {
return char.repeat(Math.max(0, length - this.length)) + this;
}
String#repeat is currently supported in Firefox and Chrome only. for other implementation, one might consider the following simple polyfill:
String.prototype.repeat = String.prototype.repeat || function(n){
return n<=1 ? this : (this + this.repeat(n-1));
}
Using the ECMAScript 6 method String#repeat and Arrow functions, a pad function is as simple as:
var leftPad = (s, c, n) => c.repeat(n - s.length) + s;
leftPad("foo", "0", 5); //returns "00foo"
jsfiddle
edit:
suggestion from the comments:
const leftPad = (s, c, n) => n - s.length > 0 ? c.repeat(n - s.length) + s : s;
this way, it wont throw an error when s.lengthis greater than n
edit2:
suggestion from the comments:
const leftPad = (s, c, n) =>{ s = s.toString(); c = c.toString(); return s.length > n ? s : c.repeat(n - s.length) + s; }
this way, you can use the function for strings and non-strings alike.
The key trick in both those solutions is to create an array instance with a given size (one more than the desired length), and then to immediately call the join() method to make a string. The join() method is passed the padding string (spaces probably). Since the array is empty, the empty cells will be rendered as empty strings during the process of joining the array into one result string, and only the padding will remain. It's a really nice technique.
With ES8, there are two options for padding.
You can check them in the documentation.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
Taking up Samuel's ideas, upward here. And remember an old SQL script, I tried with this:
a=1234;
'0000'.slice(a.toString().length)+a;
It works in all the cases I could imagine:
a= 1 result 0001
a= 12 result 0012
a= 123 result 0123
a= 1234 result 1234
a= 12345 result 12345
a= '12' result 0012
Pad with default values
I noticed that I mostly need the padLeft for time conversion / number padding.
So I wrote this function:
function padL(a, b, c) { // string/number, length=2, char=0
return (new Array(b || 2).join(c || 0) + a).slice(-b)
}
This simple function supports Number or String as input.
The default pad is two characters.
The default char is 0.
So I can simply write:
padL(1);
// 01
If I add the second argument (pad width):
padL(1, 3);
// 001
The third parameter (pad character)
padL('zzz', 10, 'x');
// xxxxxxxzzz
#BananaAcid: If you pass a undefined value or a 0 length string, you get 0undefined, so:
As suggested
function padL(a, b, c) { // string/number, length=2, char=0
return (new Array((b || 1) + 1).join(c || 0) + (a || '')).slice(-(b || 2))
}
But this can also be achieved in a shorter way.
function padL(a, b, c) { // string/number, length=2, char=0
return (new Array(b || 2).join(c || 0) + (a || c || 0)).slice(-b)
}
It also works with:
padL(0)
padL(NaN)
padL('')
padL(undefined)
padL(false)
And if you want to be able to pad in both ways:
function pad(a, b, c, d) { // string/number, length=2, char=0, 0/false=Left-1/true=Right
return a = (a || c || 0), c = new Array(b || 2).join(c || 0), d ? (a + c).slice(0, b) : (c + a).slice(-b)
}
which can be written in a shorter way without using slice.
function pad(a, b, c, d) {
return a = (a || c || 0) + '', b = new Array((++b || 3) - a.length).join(c || 0), d ? a+b : b+a
}
/*
Usage:
pad(
input // (int or string) or undefined, NaN, false, empty string
// default:0 or PadCharacter
// Optional
,PadLength // (int) default:2
,PadCharacter // (string or int) default:'0'
,PadDirection // (bolean) default:0 (padLeft) - (true or 1) is padRight
)
*/
Now if you try to pad 'averylongword' with 2... that’s not my problem.
I said that I would give you a tip.
Most of the time, if you pad, you do it for the same value N times.
Using any type of function inside a loop slows down the loop!!!
So if you just want to pad left some numbers inside a long list, don't use functions to do this simple thing.
Use something like this:
var arrayOfNumbers = [1, 2, 3, 4, 5, 6, 7],
paddedArray = [],
len = arrayOfNumbers.length;
while(len--) {
paddedArray[len] = ('0000' + arrayOfNumbers[len]).slice(-4);
}
If you don't know how the maximum padding size based on the numbers inside the array.
var arrayOfNumbers = [1, 2, 3, 4, 5, 6, 7, 49095],
paddedArray = [],
len = arrayOfNumbers.length;
// Search the highest number
var arrayMax = Function.prototype.apply.bind(Math.max, null),
// Get that string length
padSize = (arrayMax(arrayOfNumbers) + '').length,
// Create a Padding string
padStr = new Array(padSize).join(0);
// And after you have all this static values cached start the loop.
while(len--) {
paddedArray[len] = (padStr + arrayOfNumbers[len]).slice(-padSize); // substr(-padSize)
}
console.log(paddedArray);
/*
0: "00001"
1: "00002"
2: "00003"
3: "00004"
4: "00005"
5: "00006"
6: "00007"
7: "49095"
*/
padding string has been inplemented in new javascript version.
str.padStart(targetLength [, padString])
https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/padStart
If you want your own function check this example:
const myString = 'Welcome to my house';
String.prototype.padLeft = function(times = 0, str = ' ') {
return (Array(times).join(str) + this);
}
console.log(myString.padLeft(12, ':'));
//:::::::::::Welcome to my house
Here is a build in method you can use -
str1.padStart(2, '0')
Here's a simple function that I use.
var pad=function(num,field){
var n = '' + num;
var w = n.length;
var l = field.length;
var pad = w < l ? l-w : 0;
return field.substr(0,pad) + n;
};
For example:
pad (20,' '); // 20
pad (321,' '); // 321
pad (12345,' '); //12345
pad ( 15,'00000'); //00015
pad ( 999,'*****'); //**999
pad ('cat','_____'); //__cat
A short way:
(x=>(new Array(int-x.length+1)).join(char)+x)(String)
Example:
(x=>(new Array(6-x.length+1)).join("0")+x)("1234")
return: "001234"
Here is a simple answer in basically one line of code.
var value = 35 // the numerical value
var x = 5 // the minimum length of the string
var padded = ("00000" + value).substr(-x);
Make sure the number of characters in you padding, zeros here, is at least as many as your intended minimum length. So really, to put it into one line, to get a result of "00035" in this case is:
var padded = ("00000" + 35).substr(-5);
ES7 is just drafts and proposals right now, but if you wanted to track compatibility with the specification, your pad functions need:
Multi-character pad support.
Don't truncate the input string
Pad defaults to space
From my polyfill library, but apply your own due diligence for prototype extensions.
// Tests
'hello'.lpad(4) === 'hello'
'hello'.rpad(4) === 'hello'
'hello'.lpad(10) === ' hello'
'hello'.rpad(10) === 'hello '
'hello'.lpad(10, '1234') === '41234hello'
'hello'.rpad(10, '1234') === 'hello12341'
String.prototype.lpad || (String.prototype.lpad = function(length, pad)
{
if(length < this.length)
return this;
pad = pad || ' ';
let str = this;
while(str.length < length)
{
str = pad + str;
}
return str.substr( -length );
});
String.prototype.rpad || (String.prototype.rpad = function(length, pad)
{
if(length < this.length)
return this;
pad = pad || ' ';
let str = this;
while(str.length < length)
{
str += pad;
}
return str.substr(0, length);
});
Array manipulations are really slow compared to simple string concat. Of course, benchmark for your use case.
function(string, length, pad_char, append) {
string = string.toString();
length = parseInt(length) || 1;
pad_char = pad_char || ' ';
while (string.length < length) {
string = append ? string+pad_char : pad_char+string;
}
return string;
};
A variant of #Daniel LaFavers' answer.
var mask = function (background, foreground) {
bg = (new String(background));
fg = (new String(foreground));
bgl = bg.length;
fgl = fg.length;
bgs = bg.substring(0, Math.max(0, bgl - fgl));
fgs = fg.substring(Math.max(0, fgl - bgl));
return bgs + fgs;
};
For example:
mask('00000', 11 ); // '00011'
mask('00011','00' ); // '00000'
mask( 2 , 3 ); // '3'
mask('0' ,'111'); // '1'
mask('fork' ,'***'); // 'f***'
mask('_____','dog'); // '__dog'
If you don't mind including a utility library, lodash library has _.pad, _.padLeft and _.padRight functions.
I think its better to avoid recursion because its costly.
function padLeft(str,size,padwith) {
if(size <= str.length) {
// not padding is required.
return str;
} else {
// 1- take array of size equal to number of padding char + 1. suppose if string is 55 and we want 00055 it means we have 3 padding char so array size should be 3 + 1 (+1 will explain below)
// 2- now join this array with provided padding char (padwith) or default one ('0'). so it will produce '000'
// 3- now append '000' with orginal string (str = 55), will produce 00055
// why +1 in size of array?
// it is a trick, that we are joining an array of empty element with '0' (in our case)
// if we want to join items with '0' then we should have at least 2 items in the array to get joined (array with single item doesn't need to get joined).
// <item>0<item>0<item>0<item> to get 3 zero we need 4 (3+1) items in array
return Array(size-str.length+1).join(padwith||'0')+str
}
}
alert(padLeft("59",5) + "\n" +
padLeft("659",5) + "\n" +
padLeft("5919",5) + "\n" +
padLeft("59879",5) + "\n" +
padLeft("5437899",5));
It's 2014, and I suggest a JavaScript string-padding function. Ha!
Bare-bones: right-pad with spaces
function pad (str, length) {
var padding = (new Array(Math.max(length - str.length + 1, 0))).join(" ");
return str + padding;
}
Fancy: pad with options
/**
* #param {*} str Input string, or any other type (will be converted to string)
* #param {number} length Desired length to pad the string to
* #param {Object} [opts]
* #param {string} [opts.padWith=" "] Character to use for padding
* #param {boolean} [opts.padLeft=false] Whether to pad on the left
* #param {boolean} [opts.collapseEmpty=false] Whether to return an empty string if the input was empty
* #returns {string}
*/
function pad(str, length, opts) {
var padding = (new Array(Math.max(length - (str + "").length + 1, 0))).join(opts && opts.padWith || " "),
collapse = opts && opts.collapseEmpty && !(str + "").length;
return collapse ? "" : opts && opts.padLeft ? padding + str : str + padding;
}
Usage (fancy):
pad("123", 5);
// Returns "123 "
pad(123, 5);
// Returns "123 " - non-string input
pad("123", 5, { padWith: "0", padLeft: true });
// Returns "00123"
pad("", 5);
// Returns " "
pad("", 5, { collapseEmpty: true });
// Returns ""
pad("1234567", 5);
// Returns "1234567"
/**************************************************************************************************
Pad a string to pad_length fillig it with pad_char.
By default the function performs a left pad, unless pad_right is set to true.
If the value of pad_length is negative, less than, or equal to the length of the input string, no padding takes place.
**************************************************************************************************/
if(!String.prototype.pad)
String.prototype.pad = function(pad_char, pad_length, pad_right)
{
var result = this;
if( (typeof pad_char === 'string') && (pad_char.length === 1) && (pad_length > this.length) )
{
var padding = new Array(pad_length - this.length + 1).join(pad_char); //thanks to http://stackoverflow.com/questions/202605/repeat-string-javascript/2433358#2433358
result = (pad_right ? result + padding : padding + result);
}
return result;
}
And then you can do:
alert( "3".pad("0", 3) ); //shows "003"
alert( "hi".pad(" ", 3) ); //shows " hi"
alert( "hi".pad(" ", 3, true) ); //shows "hi "
If you just want a very simple hacky one-liner to pad, just make a string of the desired padding character of the desired max padding length and then substring it to the length of what you want to pad.
Example: padding the string store in e with spaces to 25 characters long.
var e = "hello"; e = e + " ".substring(e.length)
Result: "hello "
If you want to do the same with a number as input just call .toString() on it before.
A friend asked about using a JavaScript function to pad left. It turned into a little bit of an endeavor between some of us in chat to code golf it. This was the result:
function l(p,t,v){
v+="";return v.length>=t?v:l(p,t,p+v);
}
It ensures that the value to be padded is a string, and then if it isn't the length of the total desired length it will pad it once and then recurse. Here is what it looks like with more logical naming and structure
function padLeft(pad, totalLength, value){
value = value.toString();
if( value.length >= totalLength ){
return value;
}else{
return padLeft(pad, totalLength, pad + value);
}
}
The example we were using was to ensure that numbers were padded with 0 to the left to make a max length of 6. Here is an example set:
function l(p,t,v){v+="";return v.length>=t?v:l(p,t,p+v);}
var vals = [6451,123,466750];
var pad = l(0,6,vals[0]);// pad with 0's, max length 6
var pads = vals.map(function(i){ return l(0,6,i) });
document.write(pads.join("<br />"));
A little late, but thought I might share anyway. I found it useful to add a prototype extension to Object. That way I can pad numbers and strings, left or right. I have a module with similar utilities I include in my scripts.
// include the module in your script, there is no need to export
var jsAddOns = require('<path to module>/jsAddOns');
~~~~~~~~~~~~ jsAddOns.js ~~~~~~~~~~~~
/*
* method prototype for any Object to pad it's toString()
* representation with additional characters to the specified length
*
* #param padToLength required int
* entire length of padded string (original + padding)
* #param padChar optional char
* character to use for padding, default is white space
* #param padLeft optional boolean
* if true padding added to left
* if omitted or false, padding added to right
*
* #return padded string or
* original string if length is >= padToLength
*/
Object.prototype.pad = function(padToLength, padChar, padLeft) {
// get the string value
s = this.toString()
// default padToLength to 0
// if omitted, original string is returned
padToLength = padToLength || 0;
// default padChar to empty space
padChar = padChar || ' ';
// ignore padding if string too long
if (s.length >= padToLength) {
return s;
}
// create the pad of appropriate length
var pad = Array(padToLength - s.length).join(padChar);
// add pad to right or left side
if (padLeft) {
return pad + s;
} else {
return s + pad;
}
};
Never insert data somewhere (especially not at beginning, like str = pad + str;), since the data will be reallocated everytime. Append always at end!
Don't pad your string in the loop. Leave it alone and build your pad string first. In the end concatenate it with your main string.
Don't assign padding string each time (like str += pad;). It is much faster to append the padding string to itself and extract first x-chars (the parser can do this efficiently if you extract from first char). This is exponential growth, which means that it wastes some memory temporarily (you should not do this with extremely huge texts).
if (!String.prototype.lpad) {
String.prototype.lpad = function(pad, len) {
while (pad.length < len) {
pad += pad;
}
return pad.substr(0, len-this.length) + this;
}
}
if (!String.prototype.rpad) {
String.prototype.rpad = function(pad, len) {
while (pad.length < len) {
pad += pad;
}
return this + pad.substr(0, len-this.length);
}
}
Here is a JavaScript function that adds a specified number of paddings with a custom symbol. The function takes three parameters.
padMe --> string or number to left pad
pads --> number of pads
padSymble --> custom symbol, default is "0"
function leftPad(padMe, pads, padSymble) {
if(typeof padMe === "undefined") {
padMe = "";
}
if (typeof pads === "undefined") {
pads = 0;
}
if (typeof padSymble === "undefined") {
padSymble = "0";
}
var symble = "";
var result = [];
for(var i=0; i < pads; i++) {
symble += padSymble;
}
var length = symble.length - padMe.toString().length;
result = symble.substring(0, length);
return result.concat(padMe.toString());
}
Here are some results:
> leftPad(1)
"1"
> leftPad(1, 4)
"0001"
> leftPad(1, 4, "0")
"0001"
> leftPad(1, 4, "#")
"###1"
Yet another take at with combination of a couple of solutions:
/**
* pad string on left
* #param {number} number of digits to pad, default is 2
* #param {string} string to use for padding, default is '0' *
* #returns {string} padded string
*/
String.prototype.paddingLeft = function (b, c) {
if (this.length > (b||2))
return this + '';
return (this || c || 0) + '', b = new Array((++b || 3) - this.length).join(c || 0), b + this
};
/**
* pad string on right
* #param {number} number of digits to pad, default is 2
* #param {string} string to use for padding, default is '0' *
* #returns {string} padded string
*/
String.prototype.paddingRight = function (b, c) {
if (this.length > (b||2))
return this + '';
return (this||c||0) + '', b = new Array((++b || 3) - this.length).join(c || 0), this + b
};
I am trying to create a alphanumeric serial number in Javascript, the serial number is governed by the following rules:
3-Digit Alphanumeric Series
Allowed values 1-9 (Zero is excluded) and A-Z (All Capitals with exclusions of I and O)
The code should be able to give the next number after getting the input number.
The last part is tricky, basically the code would fetch the existing value of the serial number and it would then give the output as the next number.
For example: If the input number 11D then the output number should be 11E. Please let me know if this description is good enough to explain my requirement.
The excel sheet for the same is attached here
Also the part of the code where the script would fetch the starting value 11D would be from this code:
cur_frm.add_fetch('item_group','serial_number','serial_number');
This should do it:
var nextSerialNumber = function(serialNumber) {
return (parseInt(serialNumber, 36) + 1).toString(36).replace(
/i/g,'j').replace(/o/g, 'p').replace(/0/g, '1').toUpperCase();
}
nextSerialNumber("99Z") //=> "9A1"
nextSerialNumber("11D") //=> "11E"
I'm not sure what you want to happen after ZZZ. It jumps to 1111, but that could be changed.
If you input an invalid serial number (e.g. 11I), it gives you the next valid number (e.g. 11J).
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
var alphabetLen = alphabet.length;
function nextDigit(digit) {
nextDigitPos = (alphabet.indexOf(digit)+1) % alphabetLen;
return alphabet.charAt(nextDigitPos);
}
/**
* Computes the next serial id.
* #param id the id to compute the successor of,
* if null or empty String the first id
* "111" is returned.
*/
function nextSerial(id) {
if(id==null || id.length==0) return "111";
var digits = id.split("");
digits[2] = nextDigit(digits[2]);
if(digits[2] == "1") /* overflow */ {
digits[1] = nextDigit(digits[1]);
if(digits[1] == "1") /* overflow */ {
digits[0] = nextDigit(digits[0])
}
}
return digits.join("");
}
This should do it:
function getNext(num) {
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
var digits = num.toUpperCase().split(""),
len = digits.length,
increase = true;
if (len != 3)
throw new Error("Invalid serial number length in getNext: "+num);
for (var i=len-1; increase && i>=0; i--) {
var val = alphabet.indexOf(digits[i]);
if (val == -1)
throw new Error("Invalid serial number digit in getNext: "+num);
val++;
if (val < alphabet.length) {
digits[i] = alphabet[val];
increase = false;
} else { // overflow
digits[i] = alphabet[0];
}
}
if (increase) // is still true
throw new Error("Serial number overflow in getNext");
num = digits.join("");
return num;
}
Since you are working with a nearly alphanumeric alphabet, a parseInt/toString with radix 33 might have done it as well. Only you need to "jump" over the 0, I and O, that means replacing 0,A,B… by A,B,C…, replacing H,I,J… by J,K,L… and replacing M,N,O… by P,Q,R… (and everything back on deserialisation) - which might be OK if JS has a numeric char datatype, but I think it's easier to do it manually as above.
If you're curious:
String.prototype.padLeft = function(n, x) {
return (new Array(n).join(x || "0")+this).slice(-n);
};
function getNext(num) {
var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
var back = {}, forth = {};
for (var i=0; i<alphabet.length; i++) {
var a = alphabet[i],
b = i.toString(36);
back[a] = b;
forth[b] = a;
}
return (parseInt(num.replace(/./g, function(c) {
return back[c]; // base33 from alphabet
}), alphabet.length) + 1)
.toString(alphabet.length)
.padLeft(3)
.replace(/./g, function(c) {
return forth[c]; // base33 to alphabet
});
}
I've the following regular expressions that I want to run on the example input, to replace both numbers and fractions (I need to multiply them by a number that the user will choose):
numbersRegex = /[0-9]+(?:\.[0-9]*)?/g;
fractionsRegex = /((\d+)\/(\d+))/g;
The numbersRegex needs to run on lines containing numbers only (I marked them in the example input underneath using numbersRegex).
The fractionsRegex needs to run on lines containing fractions only (I marked them in the example input underneath using fractionsRegex).
I need them to be two different ones as the multiplication method is different, so I would like to run str.replace two times, one to multiply all fractions and the other to multiply all numbers.
Here is the example input:
1 1/2 oz. white rum (fractionsRegex)
1/2 lime, cut in 4 wedges (fractionsRegex)
10 mint leaves, fresh (numbersRegex)
2 tbsp. white sugar (numbersRegex)
1 cup ice cubes (numbersRegex)
1/2 cup club soda (fractionsRegex)
Is that possible?
Many thanks
UPDATE
This is the function I'm currently using (it needs to be cleaned up and I'm sure it can also be optimised):
function _increaseServings(target, initialServings, newServings) {
target.html(originalIngredientsHTML);
target.find('li').each(function(i) {
var currentLine = $(this).text();
var importantPart = currentLine.split(',');
var fractionsRegex = importantPart[0].match(/((\d+)\/(\d+))/g);
var lineHiddenFractions = importantPart[0] != null ? importantPart[0].replace(/([0-9]{1,})([\/]{1})([0-9]{1,})/g, "") : "";
var numbersRegex = lineHiddenFractions.match(/([0-9]+(?:\.[0-9]*)?)/g);
var result = {};
var strToReplace = '';
if (fractionsRegex == null && numbersRegex == null) return;
if (fractionsRegex !== null && fractionsRegex.length > 0) {
result.fraction = fractionsRegex[0];
strToReplace = result.fraction;
}
if (numbersRegex !== null && numbersRegex.length > 0) {
result.number = parseInt(numbersRegex[0]);
if(result.fraction) {
strToReplace = result.number + ' ' + strToReplace;
} else {
strToReplace = result.number;
}
}
if(result.fraction) {
var fraction = result.fraction.split('/');
if(result.number && result.fraction) {
result.decimal = parseInt(result.number) + parseInt(fraction[0]) / parseInt(fraction[1]);
} else {
result.decimal = parseInt(fraction[0]) / parseInt(fraction[1]);
}
} else {
result.decimal = parseInt(result.number);
}
result.stringToReplace = strToReplace;
var newValue = result.decimal * (newServings / initialServings);
if(newValue % 1 != 0) {
var values = String(newValue).split('.');
var checkValue = String(values[1]).slice(0,1);
var integerPart = Math.floor(newValue);
if(checkValue == 2) {
// 25
newValue = integerPart + ' 1/4';
} else if(checkValue == 3) {
// 33
newValue = integerPart + ' 1/3';
} else if(checkValue == 5) {
// 50
newValue = integerPart + ' 1/2';
} else if(checkValue == 6) {
// 66
newValue = integerPart + ' 2/3';
} else if(checkValue == 7) {
// 75
newValue = integerPart + ' 3/4';
}
if(integerPart == 0) newValue = newValue.slice(2, newValue.length);
}
currentLine = currentLine.replace(strToReplace, newValue);
$(this).text(currentLine);
});
}
Unfortunately, javascript regular expressions don't support negative lookbehinds, so you will get false positives on your fraction numbers when you are looking for just numbers. There's no way to say something like...
"/(?<!\/)([0-9])/"
...in javascript, so you will end up picking up some of the digits from the fraction.
You have to fake out the results with something like this:
Assuming your text is terminated with \n just for testing purposes:
var t = "1 1/2 oz. white rum (fractionsRegex)\n1/2 lime, cut in 4 wedges (fractionsRegex)\n10 mint leaves, fresh (numbersRegex)\n2 tbsp. white sugar (numbersRegex)\n1 cup ice cubes (numbersRegex)\n1/2 cup club soda (fractionsRegex)";
var ary = t.split("\n");
for (var i = 0; i < ary.length; i++) {
var fractionsRegex = ary[i].match(/((\d+)\/(\d+))/g);
var lineHiddenFractions = ary[i] != null ? ary[i].replace(/([0-9]{1,})([\/]{1})([0-9]{1,})/g, "") : "";
var numbersRegex = lineHiddenFractions.match(/([0-9]+(?:\.[0-9]*)?)/g);
if (fractionsRegex !== null && fractionsRegex.length > 0) {
// We have a fraction for this line.
for (var j = 0; j < fractionsRegex.length; j++) {
alert("Fraction on line " + i + ": " + fractionsRegex[j]);
}
}
if (numbersRegex !== null && numbersRegex.length > 0) {
// We have a number for this line.
for (var k = 0; k < numbersRegex.length; k++) {
alert("Number on line " + i + ": " + numbersRegex[k]);
}
}
}
I think this method offers you the most flexibility for what you want to do. For each line, you will get an array of all of your fractions and an array of all of your numbers. You can place booleans to check if the line has both numbers and fractions or just one or the other. If you need to perform calculations, you can create formulas from the arrays of fractions and numbers from each line.
to calculate
function measure(string){
var match = string.match(/^\d+(\s*\d*(\/+\d+))*/);
if(match){
match = match[0].split(" ");
var m = 0;
while(match.length){
m += eval(match.shift());
}
return m;
}
return 0;
}
or to get the values separately
function get(string){
var match = string.match(/^\d+(\s*\d*(\/+\d+))*/);
if(match){
match = match[0].split(" ");
var m = 0;
while(match.length){
m += eval(match.shift());
}
return {integer:Math.floor(m), fraction:m-Math.floor(m)};
}
return {integer:0, fraction:0};
}
or to replace integer part and fraction part seperately
function replaceInteger(string, newInt){
return string.replace(/^\d+\s+/, function(m, n){
return newInt + " ";
});
}
function replaceFraction(string, newFract){
return string.replace(/^\d+\/+\d+\s+/, function(m, n){
return newFract + " ";
});
}
replaceInteger("10 mint leaves, fresh", "100");
replaceFraction("1/2 lime leaves, fresh", "1/3");
I think this approach will do what you need. Just call getQuantity, it will return an object which may or may not contain properties number and fraction. Then once you get the result, you can perform logic to determine what to do with it.
function getQuantity(recipe){
var regex = /^(([1-9]+\d*\s)?((\d+)\/(\d+)\s)?).*$/g;
var results = regex.exec(recipe);
if(results[2] && results[5]){
return {number: Number(results[2]), fraction: Number(results[4]) / Number(results[5])};
} if(results[5]){
return {fraction: Number(results[4]) / Number(results[5])};
} else{
return {number: Number(results[2])};
}
}
getQuantity("1 1/2 oz. white rum"); // {number: 1, fraction: 0.5}
getQuantity("1/2 lime, cut in 4 wedges"); // {fraction: 0.5}
getQuantity("10 mint leaves, fresh"); // {number: 10}
getQuantity("2 tbsp. white sugar"); // {number: 2}
getQuantity("1 cup ice cubes"); // {number: 1}
getQuantity("1/2 cup club soda"); // {fraction: 0.5}
What would be the best approach to creating a 8 character random password containing a-z, A-Z and 0-9?
Absolutely no security issues, this is merely for prototyping, I just want data that looks realistic.
I was thinking a for (0 to 7) Math.random to produce ASCII codes and convert them to characters. Do you have any other suggestions?
I would probably use something like this:
function generatePassword() {
var length = 8,
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
retVal = "";
for (var i = 0, n = charset.length; i < length; ++i) {
retVal += charset.charAt(Math.floor(Math.random() * n));
}
return retVal;
}
That can then be extended to have the length and charset passed by a parameter.
Real Quick-n-dirty™
Math.random().toString(36).slice(2, 10)
Voilà! 8 random alphanumeric characters.
The idea is to cast a random number (in the range 0..1) to a base36 string (lowercase a-z plus 0-9), and then fetch the first 8 characters after the leading zero and decimal point.
However, please be aware that different browsers and javascript implementations used to give different bit depth results for Math.random(). If you are running in an old pre-2016 chrome or pre-2017 safari browser, this might mean (in worst case scenario) you get a shorter password than 8 characters. Though, you could solve this by simply concatenating two strings, and then slice it back down to 8 characters again.
A better solution
Though, please be aware that Math.random() was never designed or meant to be cryptographically secure. Since you only want passwords 8 characters long, I assume you're not interested in this in any case. However, for reference (and everyone else), I'll show a solution based on an actual CSPRNG. The idea is the same, we're just utilizing window.crypto instead.
window.crypto.getRandomValues(new BigUint64Array(1))[0].toString(36)
Here we are generating 1 word with 64 bits of random data, and cast it to a base36 string (0-9 and a-z). It should give you a truly random string roughly 10-13 characters long.
Extending the solution
However, to make it more secure we also want it to be longer and with mixed upper and lower cases.
We could do this either by just repeating the process twice:
let strings = window.crypto.getRandomValues(new BigUint64Array(2));
console.log(strings[0].toString(36) + strings[1].toString(36).toUpperCase());
Or we could make a fancy generic generator which uses Array.reduce to concatenate multiple random 64 bit words, alternating between uppercasing each stanza:
window.crypto.getRandomValues(new BigUint64Array(length)).reduce(
(prev, curr, index) => (
!index ? prev : prev.toString(36)
) + (
index % 2 ? curr.toString(36).toUpperCase() : curr.toString(36)
)
);
length is the number of 64 bit words to join. I generally use 4, which gives me rougly 48-52 random alphanumeric characters, upper and lower cased.
If you specifically want "special characters" included, you can optionally replace the 0-9 numbers in the uppercase stanzas with a simple replace() call.
const regx = new RegExp(/\d/, "g");
window.crypto.getRandomValues(new BigUint64Array(length)).reduce(
(prev, curr, index) => (
!index ? prev : prev.toString(36)
) + (
index % 2 ? curr.toString(36).toUpperCase().replace(regx, key => ".,:;-_()=*".charAt(key)) : curr.toString(36)
)
);
You may also optionally shuffle the final order, which is easily accomplished with this chaining "oneliner"
password.split('').sort(
() => 128 - window.crypto.getRandomValues(new Uint8Array(1))[0]
).join('')
The idea here is to split the generated string into an array of characters, and then sort that character array with cryptographical randomness, and finally joining it back into a string.
Personally, I have this little bookmarklet saved in my browser bookmarks bar, for quick and easy access whenever I need to generate a site-specific username:
javascript:(
function(){
prompt('Here is your shiny new random string:',
window.crypto.getRandomValues(new BigUint64Array(4)).reduce(
(prev, curr, index) => (
!index ? prev : prev.toString(36)
) + (
index % 2 ? curr.toString(36).toUpperCase() : curr.toString(36)
)
).split('').sort(() => 128 -
window.crypto.getRandomValues(new Uint8Array(1))[0]
).join('')
);
}
)();
Compatibility notices
BigUint64Array was added in:
Chrome/Chromium 67 in May 2018
Node 10.4 in June 2018
Firefox 68 in July 2019
Edge 79 in January 2020 (the first stable Chromium-based Edge release)
The final ECMAScript 2020 specification (ES11) in June 2020
and finally Safari 15 in September 2021.
Other JS engines are tracked on Can I Use or MDN Compatibility Table
Crypto.getRandomValues() has better support (except for Node):
Chrome 11
Edge 12
Firefox 21
Safari 5
Node 15.0
So if you're still on team IE 11 or use end-of-life node versions, you're stuck with using a polyfill, math.round() or a workaround with other types such as BigUInt32Array.
function password_generator( len ) {
var length = (len)?(len):(10);
var string = "abcdefghijklmnopqrstuvwxyz"; //to upper
var numeric = '0123456789';
var punctuation = '!##$%^&*()_+~`|}{[]\:;?><,./-=';
var password = "";
var character = "";
var crunch = true;
while( password.length<length ) {
entity1 = Math.ceil(string.length * Math.random()*Math.random());
entity2 = Math.ceil(numeric.length * Math.random()*Math.random());
entity3 = Math.ceil(punctuation.length * Math.random()*Math.random());
hold = string.charAt( entity1 );
hold = (password.length%2==0)?(hold.toUpperCase()):(hold);
character += hold;
character += numeric.charAt( entity2 );
character += punctuation.charAt( entity3 );
password = character;
}
password=password.split('').sort(function(){return 0.5-Math.random()}).join('');
return password.substr(0,len);
}
console.log( password_generator() );
This generates a little more robust password that should pass any password strength test. eg: f1&d2?I4(h1&, C1^y1)j1#G2#, j2{h6%b5#R2)
This is my function for generating a 8-character crypto-random password:
function generatePassword() {
var buf = new Uint8Array(6);
window.crypto.getRandomValues(buf);
return btoa(String.fromCharCode.apply(null, buf));
}
What it does: Retrieves 6 crypto-random 8-bit integers and encodes them with Base64.
Since the result is in the Base64 character set the generated password may consist of A-Z, a-z, 0-9, + and /.
function generatePass(pLength){
var keyListAlpha="abcdefghijklmnopqrstuvwxyz",
keyListInt="123456789",
keyListSpec="!##_",
password='';
var len = Math.ceil(pLength/2);
len = len - 1;
var lenSpec = pLength-2*len;
for (i=0;i<len;i++) {
password+=keyListAlpha.charAt(Math.floor(Math.random()*keyListAlpha.length));
password+=keyListInt.charAt(Math.floor(Math.random()*keyListInt.length));
}
for (i=0;i<lenSpec;i++)
password+=keyListSpec.charAt(Math.floor(Math.random()*keyListSpec.length));
password=password.split('').sort(function(){return 0.5-Math.random()}).join('');
return password;
}
code to generate a password with a given length (default to 8) and have at least one upper case, one lower, one number and one symbol
(2 functions and one const variable called 'Allowed')
const Allowed = {
Uppers: "QWERTYUIOPASDFGHJKLZXCVBNM",
Lowers: "qwertyuiopasdfghjklzxcvbnm",
Numbers: "1234567890",
Symbols: "!##$%^&*"
}
const getRandomCharFromString = (str) => str.charAt(Math.floor(Math.random() * str.length))
/**
* the generated password will be #param length, which default to 8,
* and will have at least one upper, one lower, one number and one symbol
* #param {number} length - password's length
* #returns a generated password
*/
const generatePassword = (length = 8) => {
let pwd = "";
pwd += getRandomCharFromString(Allowed.Uppers); // pwd will have at least one upper
pwd += getRandomCharFromString(Allowed.Lowers); // pwd will have at least one lower
pwd += getRandomCharFromString(Allowed.Numbers); // pwd will have at least one number
pwd += getRandomCharFromString(Allowed.Symbols); // pwd will have at least one symbol
for (let i = pwd.length; i < length; i++)
pwd += getRandomCharFromString(Object.values(Allowed).join('')); // fill the rest of the pwd with random characters
return pwd
}
A modern and secure solution
Be aware of answers that rely on Math.random - they are not secure. This is an old question so it's no surprise that Math.random still pops up, but you should absolutely not be using it to generate a string to secure anything. If you really need to support browsers older than IE11, you should add a fallback to get the random values from the back-end, generated using a CSPRNG.
function generatePassword(length) {
const crypto = window.crypto || window.msCrypto;
if (typeof crypto === 'undefined') {
throw new Error('Crypto API is not supported. Please upgrade your web browser');
}
const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const indexes = crypto.getRandomValues(new Uint32Array(length));
let secret = '';
for (const index of indexes) {
secret += charset[index % charset.length];
}
return secret;
}
This is a simple example. You'd probably want to add special characters to the set and maybe enforce digits or symbols to be present.
If you have lodash >= 4.0 in place there is a more elegant way of doing it
var chars = 'abcdefghkmnpqrstuvwxyz23456789';
function generatePassword(length) {
return _.sampleSize(chars, length).join('');
}
Here's my take (with Typescript) on this using the browser crypto API and enforcing a password which has at least:
1 lower case letter
1 upper case letter
1 symbol
const LOWER_CASE_CHARS = 'abcdefghijklmnopqrstuvwxyz'.split('');
const UPPER_CASE_CHARS = LOWER_CASE_CHARS.map((x) => x.toUpperCase());
const SYMBOLS = '!£$%^&*()#~:;,./?{}=-_'.split('');
const LETTERS_MIX = [...LOWER_CASE_CHARS, ...UPPER_CASE_CHARS, ...SYMBOLS];
const CHARS_LENGTH = LETTERS_MIX.length;
function containsLowerCase(str: string): boolean {
return LOWER_CASE_CHARS.some((x) => str.includes(x));
}
function containsUpperCase(str: string): boolean {
return UPPER_CASE_CHARS.some((x) => str.includes(x));
}
function containsSymbol(str: string): boolean {
return SYMBOLS.some((x) => str.includes(x));
}
function isValidPassword(password: string) {
return containsLowerCase(password) && containsUpperCase(password) && containsSymbol(password);
}
export function generateStrongPassword(length: number = 16): string {
const buff = new Uint8Array(length);
let generatedPassword = '';
do {
window.crypto.getRandomValues(buff);
generatedPassword = [...buff].map((x) => LETTERS_MIX[x % CHARS_LENGTH]).join('');
} while (!isValidPassword(generatedPassword));
return generatedPassword;
}
This will produce a realistic password if having characters [\]^_ is fine. Requires lodash and es7
String.fromCodePoint(...range(8).map(() => Math.floor(Math.random() * 57) + 0x41))
and here's without lodash
String.fromCodePoint(...Array.from({length: 8}, () => Math.floor(Math.random() * 57) + 65))
Here is a function provides you more options to set min of special chars, min of upper chars, min of lower chars and min of number
function randomPassword(len = 8, minUpper = 0, minLower = 0, minNumber = -1, minSpecial = -1) {
let chars = String.fromCharCode(...Array(127).keys()).slice(33),//chars
A2Z = String.fromCharCode(...Array(91).keys()).slice(65),//A-Z
a2z = String.fromCharCode(...Array(123).keys()).slice(97),//a-z
zero2nine = String.fromCharCode(...Array(58).keys()).slice(48),//0-9
specials = chars.replace(/\w/g, '')
if (minSpecial < 0) chars = zero2nine + A2Z + a2z
if (minNumber < 0) chars = chars.replace(zero2nine, '')
let minRequired = minSpecial + minUpper + minLower + minNumber
let rs = [].concat(
Array.from({length: minSpecial ? minSpecial : 0}, () => specials[Math.floor(Math.random() * specials.length)]),
Array.from({length: minUpper ? minUpper : 0}, () => A2Z[Math.floor(Math.random() * A2Z.length)]),
Array.from({length: minLower ? minLower : 0}, () => a2z[Math.floor(Math.random() * a2z.length)]),
Array.from({length: minNumber ? minNumber : 0}, () => zero2nine[Math.floor(Math.random() * zero2nine.length)]),
Array.from({length: Math.max(len, minRequired) - (minRequired ? minRequired : 0)}, () => chars[Math.floor(Math.random() * chars.length)]),
)
return rs.sort(() => Math.random() > Math.random()).join('')
}
randomPassword(12, 1, 1, -1, -1)// -> DDYxdVcvIyLgeB
randomPassword(12, 1, 1, 1, -1)// -> KYXTbKf9vpMu0
randomPassword(12, 1, 1, 1, 1)// -> hj|9)V5YKb=7
Gumbo's solution does not work. This one does though:
function makePasswd() {
var passwd = '';
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
for (i=1;i<8;i++) {
var c = Math.floor(Math.random()*chars.length + 1);
passwd += chars.charAt(c)
}
return passwd;
}
Randomly assigns Alpha, Numeric, Caps and Special per character then validates the password. If it doesn't contain each of the above, randomly assigns a new character from the missing element to a random existing character then recursively validates until a password is formed:
function createPassword(length) {
var alpha = "abcdefghijklmnopqrstuvwxyz";
var caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var numeric = "0123456789";
var special = "!$^&*-=+_?";
var options = [alpha, caps, numeric, special];
var password = "";
var passwordArray = Array(length);
for (i = 0; i < length; i++) {
var currentOption = options[Math.floor(Math.random() * options.length)];
var randomChar = currentOption.charAt(Math.floor(Math.random() * currentOption.length));
password += randomChar;
passwordArray.push(randomChar);
}
checkPassword();
function checkPassword() {
var missingValueArray = [];
var containsAll = true;
options.forEach(function (e, i, a) {
var hasValue = false;
passwordArray.forEach(function (e1, i1, a1) {
if (e.indexOf(e1) > -1) {
hasValue = true;
}
});
if (!hasValue) {
missingValueArray = a;
containsAll = false;
}
});
if (!containsAll) {
passwordArray[Math.floor(Math.random() * passwordArray.length)] = missingValueArray.charAt(Math.floor(Math.random() * missingValueArray.length));
password = "";
passwordArray.forEach(function (e, i, a) {
password += e;
});
checkPassword();
}
}
return password;
}
I see much examples on this page are using Math.random. This method hasn't cryptographically strong random values so it's unsecure. Instead Math.random recomended use getRandomValues or your own alhorytm.
You can use passfather. This is a package that are using much cryptographically strong alhorytmes. I'm owner of this package so you can ask some question.
passfather
I got insprired by the answers above (especially by the hint from #e.vyushin regarding the security of Math.random() ) and I came up with the following solution that uses the crypto.getRandomValues() to generate a rondom array of UInt32 values with the length of the password length.
Then, it loops through the array and devides each element by 2^32 (max value of a UInt32) to calculate the ratio between the actual value and the max. possible value. This ratio is then mapped to the charset string to determine which character of the string is picked.
console.log(createPassword(16,"letters+numbers+signs"));
function createPassword(len, charset) {
if (charset==="letters+numbers") {
var chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
} else if (charset==="letters+numbers+signs") {
var chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!§$%&/?#+-_#";
}
var arr = new Uint32Array(len);
var maxRange = Math.pow(2,32);
var passwd = '';
window.crypto.getRandomValues(arr);
for (let i=0;i<len;i++) {
var c = Math.floor(arr[i] / maxRange * chars.length + 1);
passwd += chars.charAt(c);
}
return passwd;
}
Thus, the code is able to use the advantage of the crypto-Class (improved security for the random value generation) and is adaptable to use any kind of charset the user wished. A next step would be to use regular expression strings to define the charset to be used.
Generate a random password of length 8 to 32 characters with at least 1 lower case, 1 upper case, 1 number, 1 special char (!#$&)
function getRandomUpperCase() {
return String.fromCharCode( Math.floor( Math.random() * 26 ) + 65 );
}
function getRandomLowerCase() {
return String.fromCharCode( Math.floor( Math.random() * 26 ) + 97 );
}
function getRandomNumber() {
return String.fromCharCode( Math.floor( Math.random() * 10 ) + 48 );
}
function getRandomSymbol() {
// const symbol = '!##$%^&*(){}[]=<>/,.|~?';
const symbol = '!#$&';
return symbol[ Math.floor( Math.random() * symbol.length ) ];
}
const randomFunc = [ getRandomUpperCase, getRandomLowerCase, getRandomNumber, getRandomSymbol ];
function getRandomFunc() {
return randomFunc[Math.floor( Math.random() * Object.keys(randomFunc).length)];
}
function generatePassword() {
let password = '';
const passwordLength = Math.random() * (32 - 8) + 8;
for( let i = 1; i <= passwordLength; i++ ) {
password += getRandomFunc()();
}
//check with regex
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{8,32}$/
if( !password.match(regex) ) {
password = generatePassword();
}
return password;
}
console.log( generatePassword() );
here is a simply smart code :
function generate(l) {
if (typeof l==='undefined'){var l=8;}
/* c : alphanumeric character string */
var c='abcdefghijknopqrstuvwxyzACDEFGHJKLMNPQRSTUVWXYZ12345679',
n=c.length,
/* p : special character string */
p='!##$+-*&_',
o=p.length,
r='',
n=c.length,
/* s : determinate the position of the special character */
s=Math.floor(Math.random() * (p.length-1));
for(var i=0; i<l; ++i){
if(s == i){
/* special charact insertion (random position s) */
r += p.charAt(Math.floor(Math.random() * o));
}else{
/* alphanumeric insertion */
r += c.charAt(Math.floor(Math.random() * n));
}
}
return r;
}
Simply call generate(), and it do key containing one special character (!##$+-*&_) for security.
Possible results : WJGUk$Ey, gaV7#fF7, ty_T55DD, YtrQMWveZqYyYKo_
There is more details and example in my website : https://www.bxnxg.com/minituto-01-generer-mots-de-passes-secures-facilements-en-javascript/
Stop the madness!
My pain point is that every Sign-Up tool allows a different set of special characters. Some might only allow these ##$%&* while others maybe don't allow * but do allow other things. Every password generator I've come across is binary when it comes to special characters. It allows you to either include them or not. So I wind up cycling through tons of options and scanning for outliers that don't meet the requirements until I find a password that works. The longer the password the more tedious this becomes. Finally, I have noticed that sometimes Sign-Up tools don't let you repeat the same character twice in a row but password generators don't seem to account for this. It's madness!
I made this for myself so I can just paste in the exact set of special characters that are allowed. I do not pretend this is elegant code. I just threw it together to meet my needs.
Also, I couldn't think of a time when a Sign-Up tool did not allow numbers or wasn't case sensitive so my passwords always have at least one number, one upper case letter, one lower case letter, and one special character. This means the minimum length is 4. Technically I can get around the special character requirement by just entering a letter if need be.
const getPassword = (length, arg) => {
length = document.getElementById("lengthInput").value || 16;
arg = document.getElementById("specialInput").value || "~!##$%^&*()_+-=[]{}|;:.,?><";
if (length < 4) {
updateView("passwordValue", "passwordValue", "", "P", "Length must be at least 4");
return console.error("Length must be at least 4")
} else if (length > 99) {
updateView("passwordValue", "passwordValue", "", "P", "Length must be less then 100");
return console.error("Length must be less then 100")
}
const lowercase = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
const uppercase = lowercase.join("").toUpperCase().split("");
const specialChars = arg.split("").filter(item => item.trim().length);
const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let hasNumber = false;
let hasUpper = false;
let hasLower = false;
let hasSpecial = false;
if (Number(length)) {
length = Number(length)
} else {
return console.error("Enter a valid length for the first argument.")
}
let password = [];
let lastChar;
for (let i = 0; i < length; i++) {
let char = newChar(lowercase, uppercase, numbers, specialChars);
if (char !== lastChar) {
password.push(char);
lastChar = char
if (Number(char)) {
hasNumber = true
}
if (lowercase.indexOf(char) > -1) {
hasLower = true
}
if (uppercase.indexOf(char) > -1) {
hasUpper = true
}
if (specialChars.indexOf(char) > -1) {
hasSpecial = true
}
} else {
i--
}
if (i === length - 1 && (!hasNumber || !hasUpper || !hasLower || !hasSpecial)) {
hasNumber = false;
hasUpper = false;
hasLower = false;
hasSpecial = false;
password = [];
i = -1;
}
}
function newChar(lower, upper, nums, specials) {
let set = [lower, upper, nums, specials];
let pick = set[Math.floor(Math.random() * set.length)];
return pick[Math.floor(Math.random() * pick.length)]
}
updateView("passwordValue", "passwordValue", "", "P", password.join(""));
updateView("copyPassword", "copyPassword", "", "button", "copy text");
document.getElementById("copyPassword").addEventListener("click", copyPassword);
}
const copyPassword = () => {
let text = document.getElementById("passwordValue").textContent;
navigator.clipboard.writeText(text);
};
const updateView = (targetId, newId, label, element, method = '') => {
let newElement = document.createElement(element);
newElement.id = newId;
let content = document.createTextNode(label + method);
newElement.appendChild(content);
let currentElement = document.getElementById(targetId);
let parentElement = currentElement.parentNode;
parentElement.replaceChild(newElement, currentElement);
}
document.getElementById("getPassword").addEventListener("click", getPassword);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div>
<button id="getPassword">Generate Password</button>
<input type="number" id="lengthInput" placeholder="Length">
<input type="text" id="specialInput" placeholder="Special Characters">
<p id="passwordValue"></p>
<p id="copyPassword"></p>
</div>
</body>
</html>
even shorter:
Array.apply(null, Array(8)).map(function() {
var c = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
return c.charAt(Math.random() * c.length);
}).join('');
or as function:
function generatePassword(length, charSet) {
charSet = charSet ? charSet : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789^°!"§$%&/()=?`*+~\'#,;.:-_';
return Array.apply(null, Array(length || 10)).map(function() {
return charSet.charAt(Math.random() * charSet.length);
}).join('');
}
function genPass(n) // e.g. pass(10) return 'unQ0S2j9FY'
{
let c='abcdefghijklmnopqrstuvwxyz'; c+=c.toUpperCase()+1234567890;
return [...Array(n)].map(b=>c[~~(Math.random()*62)]).join('')
}
Where n is number of output password characters; 62 is c.length and where e.g. ~~4.5 = 4 is trick for replace Math.floor
Alternative
function genPass(n) // e.g. pass(10) return 'unQ0S2j9FY'
{
let c='abcdefghijklmnopqrstuvwxyz'; c+=c.toUpperCase()+1234567890;
return '-'.repeat(n).replace(/./g,b=>c[~~(Math.random()*62)])
}
to extend characters list, add them to c e.g. to add 10 characters !$^&*-=+_? write c+=c.toUpperCase()+1234567890+'!$^&*-=+_?' and change Math.random()*62 to Math.random()*72 (add 10 to 62).
This method gives the options to change size and charset of your password.
function generatePassword(length=8, charset="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
return new Array(length)
.fill(null)
.map(()=> charset.charAt(Math.floor(Math.random() * charset.length)))
.join('');
}
console.log(generatePassword()); // 02kdFjzX
console.log(generatePassword(4)); // o8L5
console.log(generatePassword(16)); // jpPd7S09txv9b02p
console.log(generatePassword(16, "abcd1234")); // 4c4d323a31c134dd
A simple lodash solution that warranties 14 alpha, 3 numeric and 3 special characters, not repeated:
const generateStrongPassword = (alpha = 14, numbers = 3, special = 3) => {
const alphaChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numberChars = '0123456789';
const specialChars = '!"£$%^&*()-=+_?';
const pickedChars = _.sampleSize(alphaChars, alpha)
.concat(_.sampleSize(numberChars, numbers))
.concat(_.sampleSize(specialChars, special));
return _.shuffle(pickedChars).join('');
}
const myPassword = generateStrongPassword();
I also developed my own password generator, with random length (between 16 and 40 by default), strong passwords, maybe it could help.
function randomChar(string) {
return string[Math.floor(Math.random() * string.length)];
}
// you should use another random function, like the lodash's one.
function random(min = 0, max = 1) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// you could use any shuffle function, the lodash's one, or the following https://stackoverflow.com/a/6274381/6708504
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function generatePassword() {
const symbols = '§±!##$%^&*()-_=+[]{}\\|?/<>~';
const numbers = '0123456789';
const lowercaseLetters = 'abcdefghijklmnopqrstuvwxyz';
const uppercaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const minCharsGroup = 4;
const maxCharsGroup = 10;
const randomSymbols = [...Array(random(minCharsGroup, maxCharsGroup))].map(() => randomChar(symbols));
const randomNumbers = [...Array(random(minCharsGroup, maxCharsGroup))].map(() => randomChar(numbers));
const randomUppercasesLetters = [...Array(random(minCharsGroup, maxCharsGroup))].map(() => randomChar(uppercaseLetters));
const randomLowercasesLetters = [...Array(random(minCharsGroup, maxCharsGroup))].map(() => randomChar(lowercaseLetters));
const chars = [...randomSymbols, ...randomNumbers, ...randomUppercasesLetters, ...randomLowercasesLetters];
return shuffle(chars).join('');
}
const alpha = 'abcdefghijklmnopqrstuvwxyz';
const calpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const num = '1234567890';
const specials = ',.!##$%^&*';
const options = [alpha, alpha, alpha, calpha, calpha, num, num, specials];
let opt, choose;
let pass = "";
for ( let i = 0; i < 8; i++ ) {
opt = Math.floor(Math.random() * options.length);
choose = Math.floor(Math.random() * (options[opt].length));
pass = pass + options[opt][choose];
options.splice(opt, 1);
}
console.log(pass);
Length 8 characters
At least 1 Capital
At least 1 Number
At least 1 Special Character
Here's another approach based off Stephan Hoyer's solution
var _ = require('lodash');
function getRandomString(length) {
var chars = 'abcdefghkmnpqrstuvwxyz23456789';
return _.times(length, () => sample(chars)).join('');
}
Update: replacing the core Math.random() by crypto.getRandomValues and add options
Solution with scrambling:
const Allowed = {
Uppers: 'QWERTYUIOPASDFGHJKLZXCVBNM',
Lowers: 'qwertyuiopasdfghjklzxcvbnm',
Numbers: '1234567890',
Symbols: '!##$%^&*'
}
const AllowedUpperArray = Array.from(Allowed.Uppers)
const AllowedLowerArray = Array.from(Allowed.Lowers)
const AllowedNumberArray = Array.from(Allowed.Numbers)
const AllowedSymbolArray = Array.from(Allowed.Symbols)
function getCharAt(charArray, index) {
return charArray[index % charArray.length]
}
function scrambleArray(chars) {
return chars.sort(() => Math.random() - 0.5)
}
function getAllowedChars(compositionRule = {}) {
let chars = []
if (!compositionRule.upperCase?.forbidden) chars = chars.concat(AllowedUpperArray)
if (!compositionRule.lowerCase?.forbidden) chars = chars.concat(AllowedLowerArray)
if (!compositionRule.numbers?.forbidden) chars = chars.concat(AllowedNumberArray)
if (!compositionRule.symbols?.forbidden) chars = chars.concat(AllowedSymbolArray)
return chars
}
function assertAreRulesValid(compositionRule) {
const {
upperCase,
lowerCase,
numbers,
symbols
} = compositionRule
if (length < 1) throw new Error('length < 1')
if (upperCase?.min < 0) throw new Error('upperCase.min < 0')
if (lowerCase?.min < 0) throw new Error('lowerCase.min < 0')
if (numbers?.min < 0) throw new Error('numbers.min < 0')
if (symbols?.min < 0) throw new Error('symbols.min < 0')
if (length && length < (upperCase?.min || 0 + lowerCase?.min || 0 + numbers?.min || 0 + symbols?.min || 0)) throw new Error('length < sum of min')
if (upperCase?.forbidden && lowerCase?.forbidden && numbers?.forbidden && symbols?.forbidden) throw new Error('no char type allowed')
if (upperCase?.forbidden && upperCase?.min) throw new Error('forbidden incompatible with min')
if (lowerCase?.forbidden && lowerCase?.min) throw new Error('forbidden incompatible with min')
if (symbols?.forbidden && symbols?.min) throw new Error('forbidden incompatible with min')
if (numbers?.forbidden && numbers?.min) throw new Error('forbidden incompatible with min')
}
/**
* Generates password of the given length with at least one upper, one lower, one number and one symbol.
* #param length length of the password, min 4
* #throws Error if length is less than 4
*/
function generatePassword(length = 8, compositionRule = {}) {
const {
upperCase,
lowerCase,
numbers,
symbols
} = compositionRule
const indexes = crypto.getRandomValues(new Uint32Array(length));
const chars = []
let i = 0
let lastIndex = i
while (i < upperCase?.min || 0) chars.push(getCharAt(AllowedUpperArray, indexes[i++]))
while (i < lastIndex + lowerCase?.min || 0) chars.push(getCharAt(AllowedLowerArray, indexes[i++]))
lastIndex = i
while (i < lastIndex + numbers?.min || 0) chars.push(getCharAt(AllowedNumberArray, indexes[i++]))
lastIndex = i
while (i < lastIndex + symbols?.min || 0) chars.push(getCharAt(AllowedSymbolArray, indexes[i++]))
const allowedChars = getAllowedChars(compositionRule)
while (i < length || 0) chars.push(getCharAt(allowedChars, indexes[i++]))
return scrambleArray(chars).join('')
}
const opt1 = {
upperCase: { min: 3 },
lowerCase: { forbidden: true },
numbers: { min: 2 },
symbols: { min: 1 }
}
const pwd1 = generatePassword(10, opt1)
console.log('10 characters, min 3 uppercase, 2 numbers, 1 symbol and no lowercase:', pwd1)
const opt2 = {
upperCase: { forbidden: true },
lowerCase: { forbidden: true },
numbers: { forbidden: true },
symbols: { min: 1 }
}
const pwd2 = generatePassword(5, opt2)
console.log('5 characters, min 1 symbol but upperCase, lowercase, and numbers forbidden:', pwd2)
Answers so far are overly complicated or use Math.random() or depend on another package.
I feel the world needs yet another password generator :-)
/**
* #param {number} length
* #returns {string}
*/
function generateRandomPassword(length) {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
return window.crypto.getRandomValues(new Uint8Array(length)).reduce((password, number) => {
return password + charset.charAt(number % charset.length);
}, "");
}
Valid characters are fixed but can be trivially tailored. Probability of having a digit can be increased by repeating the sequence in the charset (i.e: charset = "…vwxyz01234567890123456789").
It uses the secure getRandomValues().
It doesn't ensure the password contains at least one uppercase letter, one lowercase letter and one digit. Therefore, it might generate a real word/noun or even an offensive word. It is very unlikely with longer passwords, though. Skewing toward digits (as explained above) may not solve that issue due to l33t. Adding some special characters is the safest course if that is your concern.
PS: Should charset be more than 256 characters long, the code must use Uint16Array instead.
PPS: What's wrong with Math.random(): it is pseudo-random. The sequence is somewhat predictable. Not every possible theoretical password can be generated because the next character is determined from a computed sequence.
Here's a free, configurable Javascript class generating random passwords: Javascript Random Password Generator.
Examples
Password consisting of Lower case + upper case + numbers, 8 characters long:
var randomPassword = new RandomPassword();
document.write(randomPassword.create());
Password consisting of Lower case + upper case + numbers, 20 characters long:
var randomPassword = new RandomPassword();
document.write(randomPassword.create(20));
Password consisting of Lower case + upper case + numbers + symbols, 20 characters long:
var randomPassword = new RandomPassword();
document.write(randomPassword.create(20,randomPassword.chrLower+randomPassword.chrUpper+randomPassword.chrNumbers+randomPassword.chrSymbols));
var createPassword = function() {
var passAt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
var passArray = Array.from({length: 15})
return passArray.map(function(_, index) {
return index % 4 == 3 ? '-' : passAt.charAt(Math.random() * passAt.length)
}).join('')
}
result like:
L5X-La0-bN0-UQO
9eW-svG-OdS-8Xf
ick-u73-2s0-TMX
5ri-PRP-MNO-Z1j
Here's a quick dynamic modern solution which I thought I'll share
const generatePassword = (
passwordLength = 8,
useUpperCase = true,
useNumbers = true,
useSpecialChars = true,
) => {
const chars = 'abcdefghijklmnopqrstuvwxyz'
const numberChars = '0123456789'
const specialChars = '!"£$%^&*()'
const usableChars = chars
+ (useUpperCase ? chars.toUpperCase() : '')
+ (useNumbers ? numberChars : '')
+ (useSpecialChars ? specialChars : '')
let generatedPassword = ''
for(i = 0; i <= passwordLength; i++) {
generatedPassword += usableChars[Math.floor(Math.random() * (usableChars.length))]
}
return generatedPassword
}