Convert number to alphabet letter - javascript

I want to convert a number to its corresponding alphabet letter. For example:
1 = A
2 = B
3 = C
Can this be done in javascript without manually creating the array?
In php there is a range() function that creates the array automatically. Anything similar in javascript?

Yes, with Number#toString(36) and an adjustment.
var value = 10;
document.write((value + 9).toString(36).toUpperCase());

You can simply do this without arrays using String.fromCharCode(code) function as letters have consecutive codes. For example: String.fromCharCode(1+64) gives you 'A', String.fromCharCode(2+64) gives you 'B', and so on.

Snippet below turns the characters in the alphabet to work like numerical system
1 = A
2 = B
...
26 = Z
27 = AA
28 = AB
...
78 = BZ
79 = CA
80 = CB
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var result = ""
function printToLetter(number){
var charIndex = number % alphabet.length
var quotient = number/alphabet.length
if(charIndex-1 == -1){
charIndex = alphabet.length
quotient--;
}
result = alphabet.charAt(charIndex-1) + result;
if(quotient>=1){
printToLetter(parseInt(quotient));
}else{
console.log(result)
result = ""
}
}
I created this function to save characters when printing but had to scrap it since I don't want to handle improper words that may eventually form

Just increment letterIndex from 0 (A) to 25 (Z)
const letterIndex = 0
const letter = String.fromCharCode(letterIndex + 'A'.charCodeAt(0))
console.log(letter)

UPDATE (5/2/22): After I needed this code in a second project, I decided to enhance the below answer and turn it into a ready to use NPM library called alphanumeric-encoder. If you don't want to build your own solution to this problem, go check out the library!
I built the following solution as an enhancement to #esantos's answer.
The first function defines a valid lookup encoding dictionary. Here, I used all 26 letters of the English alphabet, but the following will work just as well: "ABCDEFG", "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", "GFEDCBA". Using one of these dictionaries will result in converting your base 10 number into a base dictionary.length number with appropriately encoded digits. The only restriction is that each of the characters in the dictionary must be unique.
function getDictionary() {
return validateDictionary("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
function validateDictionary(dictionary) {
for (let i = 0; i < dictionary.length; i++) {
if(dictionary.indexOf(dictionary[i]) !== dictionary.lastIndexOf(dictionary[i])) {
console.log('Error: The dictionary in use has at least one repeating symbol:', dictionary[i])
return undefined
}
}
return dictionary
}
}
We can now use this dictionary to encode our base 10 number.
function numberToEncodedLetter(number) {
//Takes any number and converts it into a base (dictionary length) letter combo. 0 corresponds to an empty string.
//It converts any numerical entry into a positive integer.
if (isNaN(number)) {return undefined}
number = Math.abs(Math.floor(number))
const dictionary = getDictionary()
let index = number % dictionary.length
let quotient = number / dictionary.length
let result
if (number <= dictionary.length) {return numToLetter(number)} //Number is within single digit bounds of our encoding letter alphabet
if (quotient >= 1) {
//This number was bigger than our dictionary, recursively perform this function until we're done
if (index === 0) {quotient--} //Accounts for the edge case of the last letter in the dictionary string
result = numberToEncodedLetter(quotient)
}
if (index === 0) {index = dictionary.length} //Accounts for the edge case of the final letter; avoids getting an empty string
return result + numToLetter(index)
function numToLetter(number) {
//Takes a letter between 0 and max letter length and returns the corresponding letter
if (number > dictionary.length || number < 0) {return undefined}
if (number === 0) {
return ''
} else {
return dictionary.slice(number - 1, number)
}
}
}
An encoded set of letters is great, but it's kind of useless to computers if I can't convert it back to a base 10 number.
function encodedLetterToNumber(encoded) {
//Takes any number encoded with the provided encode dictionary
const dictionary = getDictionary()
let result = 0
let index = 0
for (let i = 1; i <= encoded.length; i++) {
index = dictionary.search(encoded.slice(i - 1, i)) + 1
if (index === 0) {return undefined} //Attempted to find a letter that wasn't encoded in the dictionary
result = result + index * Math.pow(dictionary.length, (encoded.length - i))
}
return result
}
Now to test it out:
console.log(numberToEncodedLetter(4)) //D
console.log(numberToEncodedLetter(52)) //AZ
console.log(encodedLetterToNumber("BZ")) //78
console.log(encodedLetterToNumber("AAC")) //705
UPDATE
You can also use this function to take that short name format you have and return it to an index-based format.
function shortNameToIndex(shortName) {
//Takes the short name (e.g. F6, AA47) and converts to base indecies ({6, 6}, {27, 47})
if (shortName.length < 2) {return undefined} //Must be at least one letter and one number
if (!isNaN(shortName.slice(0, 1))) {return undefined} //If first character isn't a letter, it's incorrectly formatted
let letterPart = ''
let numberPart= ''
let splitComplete = false
let index = 1
do {
const character = shortName.slice(index - 1, index)
if (!isNaN(character)) {splitComplete = true}
if (splitComplete && isNaN(character)) {
//More letters existed after the numbers. Invalid formatting.
return undefined
} else if (splitComplete && !isNaN(character)) {
//Number part
numberPart = numberPart.concat(character)
} else {
//Letter part
letterPart = letterPart.concat(character)
}
index++
} while (index <= shortName.length)
numberPart = parseInt(numberPart)
letterPart = encodedLetterToNumber(letterPart)
return {xIndex: numberPart, yIndex: letterPart}
}

this can help you
static readonly string[] Columns_Lettre = new[] { "A", "B", "C"};
public static string IndexToColumn(int index)
{
if (index <= 0)
throw new IndexOutOfRangeException("index must be a positive number");
if (index < 4)
return Columns_Lettre[index - 1];
else
return index.ToString();
}

Related

getting a string length that contains unicode character exceeding 0xffff

I’m using this character, double sharp '𝄪' which unicode is 0x1d12a.
If I use it in a string, I can’t get the correct string length:
str = "F𝄪"
str.length // returns 3, even though there are 2 characters!
How do I get the function to return the correct answer, whether or not I’m using special unicode or not ?
String.prototype.codes = function() { return [...this].length };
String.prototype.chars = function() {
let GraphemeSplitter = require('grapheme-splitter');
return (new GraphemeSplitter()).countGraphemes(this);
}
console.log("F𝄪".codes()); // 2
console.log("👩‍❤️‍💋‍👩".codes()); // 8
console.log("❤️".codes()); // 2
console.log("F𝄪".chars()); // 2
console.log("👩‍❤️‍💋‍👩".chars()); // 1
console.log("❤️".chars()); // 1
That's the function I wrote to get string length in codepoint length
function nbUnicodeLength(string){
var stringIndex = 0;
var unicodeIndex = 0;
var length = string.length;
var second;
var first;
while (stringIndex < length) {
first = string.charCodeAt(stringIndex); // returns an integer between 0 and 65535 representing the UTF-16 code unit at the given index.
if (first >= 0xD800 && first <= 0xDBFF && string.length > stringIndex + 1) {
second = string.charCodeAt(stringIndex + 1);
if (second >= 0xDC00 && second <= 0xDFFF) {
stringIndex += 2;
} else {
stringIndex += 1;
}
} else {
stringIndex += 1;
}
unicodeIndex += 1;
}
return unicodeIndex;
}
To sumarize my comments:
That's just the lenght of that string.
Some chars involve other chars as well, even if it looks like a single character. "̉mủt̉ả̉̉̉t̉ẻd̉W̉ỏ̉r̉̉d̉̉".length == 24
From this (great) blog post, they have a function that will return correct length:
function fancyCount(str){
const joiner = "\u{200D}";
const split = str.split(joiner);
let count = 0;
for(const s of split){
//removing the variation selectors
const num = Array.from(s.split(/[\ufe00-\ufe0f]/).join("")).length;
count += num;
}
//assuming the joiners are used appropriately
return count / split.length;
}
console.log(fancyCount("F𝄪") == 2) // true
Javascript (and Java) strings use UTF-16 encoding.
Unicode codepoint U+0046 (F) is encoded in UTF-16 using 1 codeunit: 0x0046
Unicode codepoint U+1D12A (𝄪) is encoded in UTF-16 using 2 codeunits (known as a "surrogate pair"): 0xD834 0xDD2A
That is why you are getting a length of 3, not 2. The length counts the number of encoded codeunits, not the number of Unicode codepoints.

How do I check each individual digit within a string?

I was given the challenge of converting a string of digits into 'fake binary' on Codewars.com, and I am to convert each individual digit into a 0 or a 1, if the number is less than 5 it should become a 0, and if it's 5 or over it should become a 1. I know how to analyze the whole string's value like so:
function fakeBin(x){
if (x < 5)
return 0;
else return 1;
}
This however, analyzes the value of the whole string, how would I go about analyzing each individual digit within the string rather than the whole thing?
Note: I have already looked at the solutions on the website and don't understand them, I'm not cheating.
You can do it in one line with two simple global string replacement operations:
function fakeBin(x){
return ("" + x).replace(/[0-4]/g,'0').replace(/[5-9]/g,'1');
}
console.log(fakeBin(1259))
console.log(fakeBin(7815))
console.log(fakeBin("1234567890"))
The ("" + x) part is just to ensure you have a string to work with, so the function can take numbers or strings as input (as in my example calls above).
Simple javascript solution to achieve expected solution
function fakeBin(x){
x = x+'' ;
var z =[];
for(var i=0;i< x.length;i++){
if((x[i]*1)<5){
z[i] =0;
}else{
z[i]=1;
}
}
return z
}
console.log(fakeBin(357))
The snippet below will take a string and return a new string comprised of zeros and/or ones based on what you described.
We use a for ...of loop to traverse the input string and will add a 0 or 1 to our return array based on whether the parsed int if greater or less than 5.
Also note that we are checking and throwing an error if the character is not a number.
const word = "1639";
const stringToBinary = function(str) {
let ret = [];
for (const char of word) {
if (Number.isNaN(parseInt(char, 10))) {
throw new Error(`${char} is not a digit!`);
} else {
const intVal = parseInt(char, 10);
ret.push(intVal > 5 ? 1 : 0);
}
}
return ret.join('');
};
console.log(stringToBinary(word));
if you are in java you can use
charAt()
and you make a for with the word length and you can check one by one
for(int i = 0; i < text.length(); i++){
yourfunction(texto.charAt(i));
}
Split the string and apply the current function you have to each element of the string. You can accomplish this with map or with reduce:
function fakeBin(x) {
x = x.split('');
let toBin = x => {
if (x < 5)
return 0;
else return 1
}
return x.map(toBin).join('');
}
console.log(fakeBin("2351"));
refactored
function fakeBin(x) {
x = [...x];
let toBin = x => x < 5 ? 0 : 1;
return x.map(toBin).join('');
}
console.log(fakeBin("2351"));
reduce
function fakeBin(x) {
let toBin = x => x < 5 ? 0 : 1;
return [...x].reduce((acc,val) => acc + toBin(val), "");
}
console.log(fakeBin("23519"));
You can use String.prototype.replace() with RegExp /([0-4])|([5-9])/g to match 0-4, 5-9, replace with 0, 1 respectively
let str = "8539734222673566";
let res = str.replace(/([0-4])|([5-9])/g, (_, p1, p2) => p1 ? 0 : 1);
console.log(res);

How to create a Alphanumeric Serial Number in Javascript?

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
});
}

How to check if a digit is used in a number multiple times

Example: We have the number 1122. I would like to check that if given number contains the digit 1 more than once. In this case, it should return true.
I need the code to be flexible, it has to work with any number, like 3340, 5660, 4177 etc.
You can easily "force" JS to coerce any numeric value to a string, either by calling the toString method, or concatenating:
var someNum = 1122;
var oneCount = (someNum + '').split('1').length;
by concatenating a number to an empty string, the variable is coerced to a string, so you can use all the string methods you like (.match, .substring, .indexOf, ...).
In this example, I've chosen to split the string on each '1' char, count and use the length of the resulting array. If the the length > 2, than you know what you need to know.
var multipleOnes = ((someNum + '').split('1').length > 2);//returns a bool, true in this case
In response to your comment, to make it flexible - writing a simple function will do:
function multipleDigit(number, digit, moreThan)
{
moreThan = (moreThan || 1) + 1;//default more than 1 time, +1 for the length at the end
digit = (digit !== undefined ? digit : 1).toString();
return ((someNum + '').split(digit).length > moreThan);
}
multipleDigit(1123, 1);//returns true
multipleDigit(1123, 1, 2);//returns false
multipleDigit(223344,3);//returns 3 -> more than 1 3 in number.
Use javascript's match() method. Essentially, what you'd need to do is first convert the number to a string. Numbers don't have the RegExp methods. After that, match for the number 1 globally and count the results (match returns an array with all matched results).
​var number = 1100;
console.log(number.toString().match(/1/g).length);​
function find(num, tofind) {
var b = parseInt(num, 10);
var c = parseInt(tofind, 10);
var a = c.split("");
var times = 0;
for (var i = 0; i < a.length; i++) {
if (a[i] == b) {
times++;
}
}
alert(times);
}
find('2', '1122');
Convert the number to a string and iterate over it. Return true once a second digit has been found, for efficiency.
function checkDigitRepeat(number, digit) {
var i, count = 0;
i = Math.abs(number);
if(isNaN(i)) {
throw(TypeError('expected Number for number, got: ' + number));
}
number = i.toString();
i = Math.abs(digit);
if(isNaN(i)) {
throw(TypeError('expected Number for digit, got: ' + digit));
}
digit = i.toString();
if(digit > 9) {
throw(SyntaxError('expected a digit for digit, got a sequence of digits: ' + digit));
}
for(i = 0; i < number.length; i += 1) {
if(number[i] === digit) {
count += 1;
if(count >= 2) { return true; }
}
}
return false;
}
In the event that you want to check for a sequence of digits, your solution may lie in using regular expressions.
var myNum = '0011';
var isMultipleTimes = function(num) {
return !!num.toString().match(/(\d)\1/g);
}
console.log(isMultipleTimes(myNum));
JavaScript Match
Using #Aspiring Aqib's answer, I made a function that actually works properly and in the way I want.
The way it works is:
Example execution: multDig('221','2')
Split the number (first argument) to an array where each element is one digit.Output: ['2','2','1']
Run a for loop, which checks each of the array elements if they match with the digit (second argument), and increment the times variable if there is a match.Output: 2
Check inside the for loop if the match was detected already to improve performance on longer numbers like 2211111111111111
Return true if the number was found more than once, otherwise, return false.
And finally the code itself:
function multDig(number, digit){
var finalSplit = number.toString().split(''), times = 0;
for (i = 0; i < finalSplit.length; i++){
if (finalSplit[i] == digit){
times++
}
if (times > 1){
return true;
}
}
return false;
}

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