How to validate date as characters are entered in js - javascript

There is an input where the user must enter a date in the format dd.mm.yyyy., And I need to check that he does not write letters and other unnecessary characters. The Internet is full of examples for a ready-made date, in my case it is necessary to check as you enter. For example, the user pressed the key 2 - it appeared in the input, pressed z - nothing changed in the input. Pressed 3 - ok, pressed / - nothing has changed. I tried to write a regular expression,
((0[1-9]{0,1})|([1-2][0-9]{0,1})|(3[0-1]{0,1}))\.{ 0.1}
but I can't figure out how to check the data after the dot. Need your advice.
Here is a small example

Just updated you tags on question, as your linked example was using React, and how you do this in plain JS would be slightly different.
Rather than regEx, a slightly easier option might be to use lookups, you could then check what valid chars are available at each char position.
Below is an example.
ps. It won't catch everything, eg. 31 days in Feb, & 29/28 for leap years etc. So ideally you should check again after date fully entered.
const {useState} = React;
const anynum = '01234567890';
const num01 = '01';
const num31 = '0123';
const dot = '.';
const posValid = [
num31, //3
anynum, //1
dot, //.
num01, //1
anynum, //2
dot, //.
anynum, //2
anynum, //0
anynum, //2
anynum //2
]
function partsValid(s) {
if (s.length > posValid.length) return false;
//simple num check at pos
for (let l = 0; l < s.length; l += 1) {
const c = s[l];
if (!posValid[l].includes(c)) return false;
}
//check day
if (s.length > 1) {
const day = parseInt(s.slice(0, 2), 10);
if (!(day >= 1 && day <= 31)) return false;
}
//check month
if (s.length > 4) {
const month = parseInt(s.slice(3, 5), 10);
if (!(month >= 1 && month <= 12)) return false;
}
return true;
}
function App() {
const [value, setValue] = useState("");
const onchange = (e) => {
const nv = e.target.value;
if (partsValid(nv)) setValue(nv);
};
return (
<div>
<input onChange={onchange} value={value} placeholder="dd.mm.yyyy" />
</div>
);
}
ReactDOM.render(<App/>, document.querySelector('#mount'));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="mount"></div>

Related

NaN (not a number) when attempting output 2 decimal place for money value [duplicate]

I have a text box that will have a currency string in it that I then need to convert that string to a double to perform some operations on it.
"$1,100.00" → 1100.00
This needs to occur all client side. I have no choice but to leave the currency string as a currency string as input but need to cast/convert it to a double to allow some mathematical operations.
Remove all non dot / digits:
var currency = "-$4,400.50";
var number = Number(currency.replace(/[^0-9.-]+/g,""));
accounting.js is the way to go. I used it at a project and had very good experience using it.
accounting.formatMoney(4999.99, "€", 2, ".", ","); // €4.999,99
accounting.unformat("€ 1.000.000,00", ","); // 1000000
You can find it at GitHub
Use a regex to remove the formating (dollar and comma), and use parseFloat to convert the string to a floating point number.`
var currency = "$1,100.00";
currency.replace(/[$,]+/g,"");
var result = parseFloat(currency) + .05;
I know this is an old question but wanted to give an additional option.
The jQuery Globalize gives the ability to parse a culture specific format to a float.
https://github.com/jquery/globalize
Given a string "$13,042.00", and Globalize set to en-US:
Globalize.culture("en-US");
You can parse the float value out like so:
var result = Globalize.parseFloat(Globalize.format("$13,042.00", "c"));
This will give you:
13042.00
And allows you to work with other cultures.
I know this is an old question, but CMS's answer seems to have one tiny little flaw: it only works if currency format uses "." as decimal separator.
For example, if you need to work with russian rubles, the string will look like this:
"1 000,00 rub."
My solution is far less elegant than CMS's, but it should do the trick.
var currency = "1 000,00 rub."; //it works for US-style currency strings as well
var cur_re = /\D*(\d+|\d.*?\d)(?:\D+(\d{2}))?\D*$/;
var parts = cur_re.exec(currency);
var number = parseFloat(parts[1].replace(/\D/,'')+'.'+(parts[2]?parts[2]:'00'));
console.log(number.toFixed(2));
Assumptions:
currency value uses decimal notation
there are no digits in the string that are not a part of the currency value
currency value contains either 0 or 2 digits in its fractional part *
The regexp can even handle something like "1,999 dollars and 99 cents", though it isn't an intended feature and it should not be relied upon.
Hope this will help someone.
This example run ok
var currency = "$1,123,456.00";
var number = Number(currency.replace(/[^0-9\.]+/g,""));
console.log(number);
For anyone looking for a solution in 2021 you can use Currency.js.
After much research this was the most reliable method I found for production, I didn't have any issues so far. In addition it's very active on Github.
currency(123); // 123.00
currency(1.23); // 1.23
currency("1.23") // 1.23
currency("$12.30") // 12.30
var value = currency("123.45");
currency(value); // 123.45
typescript
import currency from "currency.js";
currency("$12.30").value; // 12.30
This is my function. Works with all currencies..
function toFloat(num) {
dotPos = num.indexOf('.');
commaPos = num.indexOf(',');
if (dotPos < 0)
dotPos = 0;
if (commaPos < 0)
commaPos = 0;
if ((dotPos > commaPos) && dotPos)
sep = dotPos;
else {
if ((commaPos > dotPos) && commaPos)
sep = commaPos;
else
sep = false;
}
if (sep == false)
return parseFloat(num.replace(/[^\d]/g, ""));
return parseFloat(
num.substr(0, sep).replace(/[^\d]/g, "") + '.' +
num.substr(sep+1, num.length).replace(/[^0-9]/, "")
);
}
Usage : toFloat("$1,100.00") or toFloat("1,100.00$")
// "10.000.500,61 TL" price_to_number => 10000500.61
// "10000500.62" number_to_price => 10.000.500,62
JS FIDDLE: https://jsfiddle.net/Limitlessisa/oxhgd32c/
var price="10.000.500,61 TL";
document.getElementById("demo1").innerHTML = price_to_number(price);
var numberPrice="10000500.62";
document.getElementById("demo2").innerHTML = number_to_price(numberPrice);
function price_to_number(v){
if(!v){return 0;}
v=v.split('.').join('');
v=v.split(',').join('.');
return Number(v.replace(/[^0-9.]/g, ""));
}
function number_to_price(v){
if(v==0){return '0,00';}
v=parseFloat(v);
v=v.toFixed(2).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
v=v.split('.').join('*').split(',').join('.').split('*').join(',');
return v;
}
You can try this
var str = "$1,112.12";
str = str.replace(",", "");
str = str.replace("$", "");
console.log(parseFloat(str));
let thousands_seps = '.';
let decimal_sep = ',';
let sanitizeValue = "R$ 2.530,55".replace(thousands_seps,'')
.replace(decimal_sep,'.')
.replace(/[^0-9.-]+/, '');
// Converting to float
// Result 2530.55
let stringToFloat = parseFloat(sanitizeValue);
// Formatting for currency: "R$ 2.530,55"
// BRL in this case
let floatTocurrency = Number(stringToFloat).toLocaleString('pt-BR', {style: 'currency', currency: 'BRL'});
// Output
console.log(stringToFloat, floatTocurrency);
I know you've found a solution to your question, I just wanted to recommend that maybe you look at the following more extensive jQuery plugin for International Number Formats:
International Number Formatter
How about simply
Number(currency.replace(/[^0-9-]+/g,""))/100;
Works with all currencies and locales. replaces all non-numeric chars (you can have €50.000,00 or $50,000.00) input must have 2 decimal places
jQuery.preferCulture("en-IN");
var price = jQuery.format(39.00, "c");
output is: Rs. 39.00
use jquery.glob.js,
jQuery.glob.all.js
Here's a simple function -
function getNumberFromCurrency(currency) {
return Number(currency.replace(/[$,]/g,''))
}
console.log(getNumberFromCurrency('$1,000,000.99')) // 1000000.99
For currencies that use the ',' separator mentioned by Quethzel Diaz
Currency is in Brazilian.
var currency_br = "R$ 1.343,45";
currency_br = currency_br.replace('.', "").replace(',', '.');
var number_formated = Number(currency_br.replace(/[^0-9.-]+/g,""));
var parseCurrency = function (e) {
if (typeof (e) === 'number') return e;
if (typeof (e) === 'string') {
var str = e.trim();
var value = Number(e.replace(/[^0-9.-]+/g, ""));
return str.startsWith('(') && str.endsWith(')') ? -value: value;
}
return e;
}
This worked for me and covers most edge cases :)
function toFloat(num) {
const cleanStr = String(num).replace(/[^0-9.,]/g, '');
let dotPos = cleanStr.indexOf('.');
let commaPos = cleanStr.indexOf(',');
if (dotPos < 0) dotPos = 0;
if (commaPos < 0) commaPos = 0;
const dotSplit = cleanStr.split('.');
const commaSplit = cleanStr.split(',');
const isDecimalDot = dotPos
&& (
(commaPos && dotPos > commaPos)
|| (!commaPos && dotSplit[dotSplit.length - 1].length === 2)
);
const isDecimalComma = commaPos
&& (
(dotPos && dotPos < commaPos)
|| (!dotPos && commaSplit[commaSplit.length - 1].length === 2)
);
let integerPart = cleanStr;
let decimalPart = '0';
if (isDecimalComma) {
integerPart = commaSplit[0];
decimalPart = commaSplit[1];
}
if (isDecimalDot) {
integerPart = dotSplit[0];
decimalPart = dotSplit[1];
}
return parseFloat(
`${integerPart.replace(/[^0-9]/g, '')}.${decimalPart.replace(/[^0-9]/g, '')}`,
);
}
toFloat('USD 1,500.00'); // 1500
toFloat('USD 1,500'); // 1500
toFloat('USD 500.00'); // 500
toFloat('USD 500'); // 500
toFloat('EUR 1.500,00'); // 1500
toFloat('EUR 1.500'); // 1500
toFloat('EUR 500,00'); // 500
toFloat('EUR 500'); // 500
Such a headache and so less consideration to other cultures for nothing...
here it is folks:
let floatPrice = parseFloat(price.replace(/(,|\.)([0-9]{3})/g,'$2').replace(/(,|\.)/,'.'));
as simple as that.
$ 150.00
Fr. 150.00
€ 689.00
I have tested for above three currency symbols .You can do it for others also.
var price = Fr. 150.00;
var priceFloat = price.replace(/[^\d\.]/g, '');
Above regular expression will remove everything that is not a digit or a period.So You can get the string without currency symbol but in case of " Fr. 150.00 " if you console for output then you will get price as
console.log('priceFloat : '+priceFloat);
output will be like priceFloat : .150.00
which is wrong so you check the index of "." then split that and get the proper result.
if (priceFloat.indexOf('.') == 0) {
priceFloat = parseFloat(priceFloat.split('.')[1]);
}else{
priceFloat = parseFloat(priceFloat);
}
function NumberConvertToDecimal (number) {
if (number == 0) {
return '0.00';
}
number = parseFloat(number);
number = number.toFixed(2).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1");
number = number.split('.').join('*').split('*').join('.');
return number;
}
This function should work whichever the locale and currency settings :
function getNumPrice(price, decimalpoint) {
var p = price.split(decimalpoint);
for (var i=0;i<p.length;i++) p[i] = p[i].replace(/\D/g,'');
return p.join('.');
}
This assumes you know the decimal point character (in my case the locale is set from PHP, so I get it with <?php echo cms_function_to_get_decimal_point(); ?>).
You should be able to handle this using vanilla JS. The Internationalization API is part of JS core: ECMAScript Internationalization API
https://www.w3.org/International/wiki/JavaScriptInternationalization
This answer worked for me: How to format numbers as currency strings

onChange event impacts String length (Sanitizer)

I am currently working on an input label, which formats and fixes an user input to a correct date-time format.
Currently, nothing except digits will be formatted to a date.
For instance: 11302020 => 11 / 30 / 2020
Now I want to set a range the string parts for day, month, year.
If a user exceeds the limit, the number (or part of the string) will be sanitized.
My function chops the input string into chunks, so I can write the values into a new array.
However, at the end my chopped array has a) size of 6 and b) an overall char length with blanks of 15. When I put these conditions in an if-question to save these values in separate parts, it starts saving at a char length of 16, which means, after an user enters the full date and an additional char, which is not what I want with my (b). Can someone help me out?
import React, { useState } from "react";
// export const dateFormatter = (input) => {
// return input;
// };
export default function App() {
const [maskedState, setMaskedState] = useState("");
const dateFormatter = (date) => {
setMaskedState(date.replace(/\D/g, "")
.replace(/(\d{2})(\d)/, " $1 / $2")
.replace(/(\d{2})(\d)/, "$1 / $2")
.replace(/(\d{4})\d+?$/, "$1"));
const maskedStateStr = maskedState.split(" ");
const charLength = 15;
const arrLength = 6;
if ((maskedStateStr.length === arrLength) && (maskedState.length === charLength)){
maskedStateStr.shift();
var day = maskedStateStr[0];
var month = maskedStateStr[2];
var year = maskedStateStr[4];
console.log(day,month,year);
}
//console.log(maskedStateStr, maskedStateStr.length, maskedState, maskedState.length)
}
const handleInput = (date) => {
const inputValue = date.target.value;
dateFormatter(inputValue);
};
return (
<div className="App">
<input onChange={handleInput} value={maskedState} />
</div>
);
}
There are so many issues with that code but I only focus on react part of it.
setMaskedState doesn't update maskedState immediately so maskedState will most likely point to a stale state.
setting value on an input renders it uneditable so you don't even see what you're typing. Use defaultValue.
That said you should operate on the date value and only set the state at the end of your block to reflect the result. Like:
export default function App() {
const [maskedState, setMaskedState] = useState(null);
const dateFormatter = value => {
let formattedDate = value
.replace(/\D/g, "")
.replace(/(\d{2})(\d)/, " $1 / $2")
.replace(/(\d{2})(\d)/, "$1 / $2")
.replace(/(\d{4})\d+?$/, "$1");
const maskedStateStr = formattedDate.split(" ");
const charLength = 15;
const arrLength = 6;
if (
maskedStateStr.length === arrLength &&
formattedDate.length === charLength
) {
maskedStateStr.shift();
let day = maskedStateStr[0];
let month = maskedStateStr[2];
let year = maskedStateStr[4];
setMaskedState(`${day} / ${month} / ${year}`);
} else {
setMaskedState(value);
}
};
const handleInput = date => {
const inputValue = date.target.value;
dateFormatter(inputValue);
};
return (
<div className="App">
<input onChange={handleInput} value={maskedState} />
<pre>
<code>
{maskedState
? JSON.stringify(maskedState, null, 2)
: "Not a valid date yet"}
</code>
</pre>
</div>
);
}
See the demo on StackBlitz

Check If a given time(Only hours:minutes) lies betwenn two other different time?

I have 2 different times:
var shiftStartTime = "05:48";
var shiftEndTime = "14:29";
And i have another time which is selectedDate ="06:20"(this will change according datetimepicker selection), and i want to check if selectedDate should be between (shiftStartTime and shiftEndTime ).
Can anyone help in this?
Updated Code:
i have 6 different timespan like below
var shift1StartTime = "05:48";
var shift1EndTime = "14:18";
var shift2StartTime = "14:30";
var shift2EndTime = "22:29";
va
r shift3StartTime = "22:30";
var shift3EndTime = "05:47";
using all 6 timespan i want to check the if the given time is between (shift1StartTime and shift1EndTime) return shift1
Or
if the given time is between (shift2StartTime and shift2EndTime) return shift2
Or
if the given time is between (shift3StartTime and shift3EndTime) return shift3
Simply compare the strings like
var shiftStartTime = "05:48"; var shiftEndTime = "14:29";
shiftStartTime > shiftEndTime // false
Here is some JS that does this, although better formatted time would make it a lot easier
function findTotalTime(time) {
hours = parseInt(time.substring(0,2))
mins = parseInt(time.substring(3,5))
return (hours*60) + mins
}
startTime = findTotalTime(shiftStartTime)
endTime = findTotalTime(shiftEndTime)
selectedTime = findTotalTime(selectedDate)
if (selectedTime > startTime && selectedTime < endTime) {
// time is inbetween shifts
}
const date = new Date();
const shiftStartTime = '05:48';
const shiftEndTime = '14:29';
const selectedDate = '14:20';
const start = date.setHours(+shiftStartTime.split(':')[0], +shiftStartTime.split(':')[1], 0, 0);
const end = date.setHours(+shiftEndTime.split(':')[0], +shiftEndTime.split(':')[1], 0, 0);
const selected = date.setHours(+selectedDate.split(':')[0], +selectedDate.split(':')[1], 0, 0);
if (start < selected && selected < end) {
console.log(true);
} else {
console.log(false);
}
Alright, so you got three relative times as strings in the format HH:mm. I'm assuming that your times are given as 24h strings / military time, because otherwise, you'd need an A.M. / P.M. specifier.
It is always useful to have the data you are working with in a well-suited machine-readable format, so you could parse them into a simple object holding the hour and minute as numbers.
A function doing this could look like this.
function parseTimeStr(time) {
// The pattern of your input,
// allows spaces around the `:`
// and single-digit inputs like `8:00`
let re = /([0-9][0-9]?)\s*:\s*([0-9][0-9]?)/;
let result = re.exec(time.trim());
if (result === null) {
throw "No match"
}
let hour = parseInt(result[1], 10);
let minute = parseInt(result[2], 10);
/* handle out of range values here */
return { hour, minute };
}
Alright, so you have these objects now. How do you compare them? There's a pattern for that: Have a function returning whether the first argument is greater (1), equal (0), or less (-1) than the second.
Writing this is simple now that the time is an object:
function cmpDate(date1, date2) {
if (date1.hour > date2.hour) {
return 1;
} else if (date1.hour < date2.hour) {
return -1;
} else if (date1.minute > date2.minute) {
return 1;
} else if (date1.minute < date2.minute) {
return -1;
} else {
return 0;
}
}
Alright, now we can have a helper function checking if the first argument is in the closed interval defined by the last two arguments:
function isInShift(time, shiftStart, shiftEnd) {
// time is greater or equal shiftStart
// and less or equal shiftEnd
return cmpDate(time, shiftStart) !== -1 && cmpDate(time, shiftEnd) !== 1;
}
You can then finally make your comparison by calling isInShift(parseTimeStr(selectedTime), parseTimeStr(shiftStartTime), parseTimeStr(shiftEndTime)). This will return a boolean. You can easily extend this infrastructure for multiple shifts.
Be aware that both reality and your users can be more ... screwy than you'd expect.
The above code does not do error handling for invalid time inputs, neither does it account for overnight shifts, but these are details that you can easily work out, you just have to put some effort into thinking of them.

Custom JS logic to validate IP ranges does not work as intended

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

javascript password generator

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
}

Categories