convert String to json object with wrong string - javascript

I have String like
"[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]"
need to convert into Json format
trying something like this
const text = "[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]";
const myArr = JSON.parse(text);
document.getElementById("demo").innerHTML = myArr[0];
But getting error :-
Uncaught SyntaxError: Expected property name or '}' in JSON at position 2

Simple Solution:
Convert all of the single-quoted strings and properties (that are invalid JSON in the specification) to valid JSON with double-quotes using a Regular Expression (RegExp):
const unformattedText = "[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]"
// convert with a regex
const formattedText = unformattedText.replace(/'/g, "\"")
console.log(formattedText)
// convert to object
const object = JSON.parse(formattedText)
console.log(object)
NOTE: As #Igor pointed out, this will only work for JSON strings that don't include properties or strings with single-quotes ', (like the OP's).
Update:
My String.prototype.replace() solution behavior:
Cannot recover any field or string
with a single-quote ' in it,
Example:
[{'name':'Bob O'Rielly','height':'13',5"'}]
Becomes:
[{"name":"Bob O"Rielly","height":"13",5""}]
which is incorrect and invalid JSON!
Over-Engineered Solution: Literally an entire parser...
It should be well commented enough to be able to use and modify to anyone's needs.
WARNING: In brute-force mode can be O(2^n)! Which is very inefficient.
The presence of incorrect string delimiters causes ambiguity during parsing. And useful solutions are very computationally expensive.
/**
* #typedef {function(StringPosition): boolean} BruteForce
* A function that takes information about the current
* position of the single-quote in the misformatted
* string the parser found.
*
* And returns a boolean whether the parser will use
* brute-force parsing, with time-complexity `O(2^n)`.
*
* #typedef {function(StringPosition): Guess} Heuristic
* A function that takes a reference the parser state
* information, and modifies the parser guess state
* using the given reference.
*
* This allows for much more complex logic.
*
* Example:
*
* Situations where the single-quote (`'`) needs to be
* replaced by an escaped double-quote (`\\"`).
*
* #typedef {("array"|"field"|"object-value"|"value")} JSONType
* A string union type JSON types for arrays, object
* fields, object values, and values.
*
* #typedef {("\""|"\'")} Quote
* One single-quote (`'`) or double-quote (`""`)
* string character.
*
* #typedef {("\\\""|"\\'")} EscapedQuote
* One single-quote (`\\'`) or double-quote (`\\"`)
* string character, preceded by a reverse solidus
* (`\`), to escape it from the usual JSON
* interpretation.
*
* #typedef {Object} StringPosition
* An object containing information on where in a
* string a single-quote character was found by the
* parser.
*
* #property {Number} StringPosition.stringIndex The index of the
* single-quote in the misformatted string the parser
* is currently trying to correct.
*
* #property {Number} StringPosition.characterIndex The UTF-32 character
* pseudo-index `index - (UFT-16 surrogate pairs /2)`
* of the single-quote in the misformatted string the
* parser is currently trying to correct.
*
* #property {String} StringPosition.followingString The JSON substring
* after the single-quote.
*
* #property {String} StringPosition.precedingString The JSON substring
* before the single-quote.
*
* #property {Guess} StringPosition.currentGuess An object containing
* information about a parser attempt to reformat a
* JSON string. It contains a reformatted substring
* the parser generated, and whether the internal
* heuristics consider the substring to end in any
* incompleted JSON type.
*
* #typedef {Object} Guess
* An object containing information about a parser
* attempt to reformat a JSON string. It contains a
* reformatted substring the parser generated, and
* whether the internal heuristics should consider the
* substring to end in an incompleted JSON string.
*
* #property {String} Guess.string The parser guess of how to reformat
* JSON string. It is set to the previously parsed
* guess, with future parser guesses appended to it.
*
* #property {Boolean} Guess.isString The internal flag for `preParse`
* that determines whether the algorithm interprets
* current parsing as parsing the middle of a JSON
* string value.
*
* #property {Boolean} Guess.isEscaped The internal flag for `preParse`
* that determines whether the algorithm interprets
* current parsing as parsing a character escape
* sequence in the middle of a JSON string value. It is
* a number to accommodate unicode escape sequences.
*
* Only works for: `\'`. not: `\"`, `\\`, `\/`, `\n`,
* `\r`, `\b`, `\f`, `\t` or `\uXXXX`.
*
* #property {Boolean} Guess.isObjectField The internal flag for
* `preParse` that determines whether the algorithm
* interprets the current parsing state as parsing
* the middle of a JSON object value.
*
* #property {Boolean} Guess.isObjectValue The internal flag for
* `preParse` that determines whether the algorithm
* interprets the current parsing state as parsing
* the middle of a JSON object field.
*
* #property {Number} Guess.isObject The internal flag for `preParse`
* that determines whether the algorithm interprets
* the current parsing state as parsing the middle of
* a JSON object. It is a number to track how many
* objects the single-quote is nested within.
*
* #property {Number} Guess.isArray The internal flag for `preParse` that
* determines whether the algorithm interprets the
* current parsing state as parsing the middle of a
* JSON array value. It is a number to track how many
* arrays the single-quote is nested within.
*
* #property {Number} Guess.aufoFilled The internal flag for `preParse`
* that determines whether the algorithm already
* reformatted this string index with a previous RegEx
* heuristic solution. Is a string for how many future
* indexes were prefilled. Default: `0`.
*
* #property {JSONType} [Guess.isJSONType] The internal flag for `preParse`
* that determines whether the algorithm interprets
* the current parsing state as parsing the middle of
* specific JSON type. It is a string union type for
* arrays, object fields, object values, and values.
* Used to keep track of the JSON type being parsed,
* without including type nesting depth information.
*/
/**
* #description Reformats a misformatted JSON string to valid JSON.
*
* **NOTE:** This function assumes that all
* double-quotes are escaped correctly, like: `\"`.
*
* If there are improperly escaped double-quotes, this
* function will not be able to reformat the
* misformatted JSON properly, unless custom logic in
* an `opts.heuristic` function is used.
*
* **NOTE:** this is _**NOT**_ designed for
* performance or large JSON strings. This can have
* `O(2^n)` time-complexity!
*
* #param {String} misformattedJSON The string that is invalidly
* formatted for JSON by using single-quotes (`'`)
* instead of double-qutoes (`"`) to delimit string
* values and object fields.
*
* #param {Heuristic} [heuristic] A function that takes a reference the
* parser state information, and modifies the parser
* guess state using the given reference. This allows
* for much more complex logic.
*
* Example:
*
* Situations where the single-quote (`'`) needs to be
* replaced by an escaped double-quote (`\\"`).
*
* #param {BruteForce} [bruteForce] A function that takes information
* about the current position of the single-quote in
* the misformatted string the parser found. And
* returns a boolean whether the parser will use
* brute-force parsing, with time-complexity `O(2^n)`.
*
* #param {Boolean} [TRY_HARD] A flag to brute-force with default,
* heuristics, with time-complexity at around:
* `O(2^n)`. Can be better than a `bruteForce`, but
* still not reccommended, use `heuristic` if possible.
*
* #returns {String[]} The reformatted string, or array of reformatted
* strings, if there was no error in parsing.
*/
function preParse(misformattedJSON, bruteForce, heuristic, TRY_HARD) {
/*
if (TRY_HARD)
console.warn(
"THIS HAS O(2^n) TIME COMPLEXITY, MAKE SURE YOU KNOW WHAT YOU'RE DOING..."
)
*/
/**
* #type {Guess[]} Array to store all of the parser's possible
* solutions. Holds strings of the parser guesses.
* Used when strings are ambiguous because of
* incorrect formatting, and brute-forcing. Is
* appended to by recursive calls to `preParse`.
*/
let guesses = [
{
string: "",
isString: false,
isEscaped: false,
isArray: 0,
isObject: 0,
isObjectField: false,
isObjectValue: false,
isJSONType: undefined,
aufoFilled: 0
}
]
/**
* #type {String[]} Fixed-width array to index each UTF-32 character.
* JavaScript strings are indexed by UTF-16 code units,
* which are 2 bytes (16 bits) in length, a single
* UTF-32 character is 4 bytes (32 bits), the same as
* a complete UTF-16 surrogate pair, (which is two
* UTF-16 code units).
*/
let fixedWidthCharacterArray = []
// iteration over each UTF-32 code point to add to UTF-32 array
for (const UTF16CodePoint of misformattedJSON)
fixedWidthCharacterArray.push(UTF16CodePoint)
// loop over UTF-32 characters
for (
let characterIndex = 0, stringIndex = 0;
characterIndex < fixedWidthCharacterArray.length;
stringIndex += fixedWidthCharacterArray[characterIndex].length,
characterIndex++
) {
/**
* #type {String} Current UTF-32 code point in character array.
*/
const character = fixedWidthCharacterArray[characterIndex]
// loop over all guesses, and evaluate thier likelihood,
// reversing over array to add or remove guesses
// without changing the portion of the array being looped over
for (let guessIndex = guesses.length - 1; guessIndex >= 0; guessIndex--) {
/**
* #type {Guess} Parsers's guess of how to reformat JSON string. It
* is set to the previous parser guess, with future
* parser guesses appended to it. It also includes
* state information used for the default heuristics.
*/
const currentGuess = guesses[guessIndex]
// check guess auto-fill
if (currentGuess.aufoFilled) {
// decrement number of pre-parsed characters,
// since one was just manually parsed
currentGuess.aufoFilled--
// guess already parsed this part of the string using a RegExp,
// loop to next guess
continue
}
const {
isArray,
isEscaped,
isObject,
isObjectField,
isObjectValue,
isString,
isJSONType
} = currentGuess
/**
* #type {String} The substring _after_ the single-quote.
*/
const followingString = misformattedJSON.slice(stringIndex + 1)
/**
* #type {String} The substring _before_ the single-quote.
*/
const precedingString = misformattedJSON.slice(0, stringIndex)
// change parser state for delimiters
if (character !== "'") {
// keep character
currentGuess.string += character
// set escape sequence, only for inside of a JSON string
if (isString) {
// escape character found,
// if another escape character preceded this one,
// the following character is not part of an escape sequence
// otherwise, the following character is part of an escape sequence
if (character === "\\") currentGuess.isEscaped = !isEscaped
// if isEscaped is false, loop to possible end of string
// if isEscaped is true, ignore escaped single-quote character later
// ignore delimiter characters inside of a JSON string,
// since they do not overlap with escape sequence
continue
}
// set if in an object field and increase object nesting
if (character === "{") {
// set if in an object field
currentGuess.isObjectField = true
currentGuess.isJSONType = "field"
// increase object nesting
currentGuess.isObject++
}
// unset in object field on delimiter and set in object value
else if (character === ":") {
// unset object field on delimiter
currentGuess.isObjectField = false
currentGuess.isJSONType = "object-value"
// set in object value
currentGuess.isObjectValue = true
}
// unset in object value on delimiter and decrease object nesting
else if (character === "}") {
// unset in object value on delimiter
currentGuess.isObjectValue = false
// decrease object nesting
currentGuess.isObject--
currentGuess.isJSONType =
// if this is nested in a root object,
!isArray && isObject
? "object-value"
: // if this is nested in a root array,
!isObject && isArray
? "array"
: // default JSON value type
isArray && isObject
? "value"
: // end of JSON
undefined
}
// increase array nesting
else if (character === "[") {
currentGuess.isArray++
currentGuess.isJSONType = "array"
}
// decrease array nesting
else if (character === "]") {
currentGuess.isArray--
currentGuess.isJSONType =
// if this is nested in a root array,
!isObject && isArray
? "array"
: // if this is nested in a root object,
!isArray && isObject
? "object-value"
: // default JSON value type
isArray && isObject
? "value"
: // end of JSON
undefined
}
// only for objects, not array in object, reset field and unset value
else if (
isObjectValue &&
isJSONType === "object-value" &&
character === ","
) {
// last value ended in last object entry
currentGuess.isObjectValue = false
// new field started in next object entry
currentGuess.isObjectField = true
currentGuess.isJSONType = "field"
}
// only attempt to reformat single-quotes
continue
}
// try all possible solutions by generating
// all possible solutions by brute-force
if (
bruteForce?.({
// make copy to prevent mutations
currentGuess: { ...currentGuess },
followingString,
precedingString,
stringIndex,
characterIndex
})
) {
// create a new guess
guesses.push({
...currentGuess,
// try to make a valid JSON string
// by keeping this single-quote,
string: currentGuess.string + character
// // possibly change these to allow for different heuristics
// isString: currentGuess.isString,
// isEscaped: currentGuess.isEscaped,
// isArray: currentGuess.isArray,
// isObject: currentGuess.isObject
// isObjectField: currentGuess.isObjectField,
// isObjectValue: currentGuess.isObjectValue,
})
// try to make a valid JSON string
// by replacing this single-quote with a double-quote
//! must come after new guess creation
currentGuess.string += '"'
// // possibly change these to allow for different heuristics
// currentGuess.isString = currentGuess.isString
// currentGuess.isEscaped = currentGuess.isEscaped
// currentGuess.isArray = currentGuess.isArray
// currentGuess.isObject = currentGuess.isObject
// currentGuess.isObjectField = currentGuess.isObjectField
// currentGuess.isObjectValue = currentGuess.isObjectValue
}
// use user given heuristics for for how to replace single-quote,
// or change guess parsing state
else if (
heuristic?.({
// give current parser guess and state info to heuristic
currentGuess,
followingString,
precedingString,
stringIndex,
characterIndex
})
) {
/* do nothing... `heuristic` modified object reference directly */
}
// use default heuristics
else if (isString) {
// skip if quote is escaped and clearly inside of a string
if (isEscaped) {
// escape sequence finished
currentGuess.isEscaped = false
// add normal quote character back to string
currentGuess.string += character
// loop to next character
continue
}
/**
* #type {String} JSON object, value, or array ending delimiters.
*/
let possibleDelimiters = ""
// add contextual delimiters for use in heuristics
// delimiter for ending an array,
if (isJSONType === "array") possibleDelimiters = ",\\]"
// delimiter for ending an object,
else if (isJSONType === "object-value") possibleDelimiters = ",}"
// delimiter for ending an object field
else if (isJSONType === "field" || isObjectField)
possibleDelimiters = ":"
// delimiter for ending a value
else if (isJSONType === "value") possibleDelimiters = ",}\\]"
/**
* #type {Boolean} This single-quote is followed by a JSON type
* delimiter, and likely should be a double-quote.
*/
const heuristicDelimiterFollowing =
!!possibleDelimiters &&
new RegExp(String.raw`^\s*[${possibleDelimiters}]`).test(
followingString
)
/**
* #type {RegExpExecArray|null} Finds a different possible solution.
* Starting from the current character, and not the
* `followingString`, so the RegExp includes it in the
* results array, leaving the capturing group with just
* the alternate possible and validated solution.
*/
const heuristicFindValids = new RegExp(
String.raw`([^']*)'\s*[${possibleDelimiters}]`
).exec(misformattedJSON.slice(stringIndex))
/**
* #type {(String|"")} The RegExp validated string capturing group of
* just the characters before the single-quote to
* replace in the RexExp auto-filled guess. Skip if
* empty string, or capturing group is empty string.
*/
const regexpCapturingGroup = heuristicFindValids
? heuristicFindValids[1]
: ""
// both this single-quote and a following single-quote
// have potentially correct resolutions, making the solution ambiguous,
// so create a new guess to try both solutions
if (regexpCapturingGroup) {
// only the following single-quote RegExp heuristic passed,
// which likely means this is a string character that should be kept,
// so use RegEpx auto-fill solution
// this guess will replace the single-quote and end the string
guesses.push({
...currentGuess,
isString: false,
string: currentGuess.string + '"'
})
if (TRY_HARD)
guesses.push({
...currentGuess,
isString: true, // stay string
string: currentGuess.string + character
})
// keep single-quote and use RegExp solution of all characters before
// single-quote from RegExp search that needs to be replaced
currentGuess.string += character + regexpCapturingGroup + '"'
// the length of the entire RegExp guess string
// including ending double-quote
currentGuess.aufoFilled = regexpCapturingGroup.length + 1
// ended string with double-quote
currentGuess.isString = false
}
// only the current single-quote RegExp heuristic passed,
// which likely means this is a misformatted string delimiter
else if (heuristicDelimiterFollowing) {
// WORSE time-complexity mode, custom heuristics but with brute-force
if (TRY_HARD)
guesses.push({
...currentGuess,
isString: false,
string: currentGuess.string + character
})
// replace current misformatted single-quote with a double-quote
currentGuess.string += '"'
// ended string with double-quote
currentGuess.isString = false
}
// neither RegExp heuristic passed,
// which likely means this is a string character that should be kept
else {
// WORSE time-complexity mode, custom heuristics but with brute-force
if (TRY_HARD)
guesses.push({
...currentGuess,
isString: false, // end string
string: currentGuess.string + character
})
// keep single-quote
currentGuess.string += character
}
}
// beginning of a JSON string, reformat single-quote
else {
// replace single-quote with double-quote
currentGuess.string += '"'
// parser now looking for ending single-quote that needs reformatting
currentGuess.isString = true
}
}
}
// try all parsing guesses to see if one works
/**
* #type {Number} Index of guess for debugging information.
*/
let guessIndex = 0
/**
* #type {String[]} Array of every valid JSON string reformatted.
*/
const successes = []
for (const guess of guesses) {
try {
JSON.parse(guess.string)
// reformatting guess parsed as valid JSON!
successes.push(guess.string)
console.log(`successfully parsed #${guessIndex}:`, guess, successes)
} catch (error) {
// // catch JSON parsing error and output for debug
// console.error(`failed: guess #${guessIndex}:`, guess)
}
guessIndex++
}
if (successes.length === 1)
console.log("Parser found definitive answer!\nParsed:", successes[0])
else if (successes.length > 1)
console.log(
"Parser found no definitive answer! Parser found ambiguous answers! Try setting `opts.heuristic` or `opts.bruteForce`, they can get better parsing!\nParsed:",
successes
)
else
console.error(
"Parser Failed to parse any reformatting guess as a valid JSON string! Try setting `heuristic`, `TRY_HARD`, or `opts.bruteForce` (if necessary), they can get better parsing!"
)
// return all valid JSON string guesses
return successes
}
const json1 = `['Bob O'Rielly']`
const json2 = `['Mr. O'McDonald, height 13',1\\"']`
const json3 = `[{'fullName':'Bob O'Rielly','height':'13',5\\"'}]`
const json4 = `[[''''''''''''],[[''''''''],'[]','{}',',',':'],'''''''''''''']`
const json5 = `[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'MANPREET SINGH','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]`
const json6 = `[{'fullName':'Rob O'Rielly','height':'70.5\\"'}]`
const json7 = `[{'fullName':'Dob MacRielly','height':'13',5\\"'}]`
console.log(`ANSWERS 1:`, preParse(json1))
console.log(`ANSWERS 2:`, preParse(json2, undefined, undefined, true))
console.log(`ANSWERS 3:`, preParse(json3, undefined, undefined, true))
console.log(`ANSWER 4:`, preParse(json4))
console.log(`ANSWER 5:`, preParse(json5))
console.log(
`ANSWER 6:`,
preParse(json6, () => true, undefined)
)
console.log(`ANSWER 7:`, preParse(json7, undefined, undefined, true))

The OP is in need of a sanitizing replace task where the used regex needs to explicitly target the single quotes around any property name and any string value. Thus such a regex has to utilize both a positive lookbehind and a positive lookahead. It would look as follows ...
/(?<=[,{:])'|'(?=[:,}])/g
... and its functioning is described at its playground page.
const invalidlyQuotedJSON =
"[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'Danny O\'Reilly','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]";
// see ... [https://regex101.com/r/gzCPaS/1]
const regXSingleQuotes = /(?<=[,{:])'|'(?=[:,}])/g;
const validJSON = invalidlyQuotedJSON.replace(regXSingleQuotes, '"');
const parsedData = JSON.parse(validJSON);
console.log({
invalidlyQuotedJSON,
validJSON,
parsedData,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
For environments which do not support lookbehinds like current Safari's one has to change the approach to two different regex patterns each featuring a capturing group for either the leading ... /([,{:])'/g ... or the trailing ... /'([:,}])/g ... character of a to be replaced single quote.
The above provided example code then changes to ...
const invalidlyQuotedJSON =
"[{'techid':'0128','daPoints':3,'speedingPoints':3,'fleetInspectionPoints':3,'lofPoints':3,'missedTrgModules':null,'fullName':'Danny O\'Reilly','safetyInspectPoints':3,'missedTrgPoints':3,'speeding_qty':null,'safetyTotalPoints':21,'atFaultPoints':3,'atFaultAccident':null,'region':'PYEM','supervisor':'AGHATOR OSA','driverAlert':null,'status':'A'}]";
// see ... [https://regex101.com/r/gzCPaS/2]
const regXTrailingSingleQuote = /([,{:])'/g;
// see ... [https://regex101.com/r/gzCPaS/3]
const regXLeadingSingleQuote = /'([:,}])/g;
const validJSON = invalidlyQuotedJSON
.replace(regXTrailingSingleQuote, '$1"')
.replace(regXLeadingSingleQuote, '"$1');
const parsedData = JSON.parse(validJSON);
console.log({
invalidlyQuotedJSON,
validJSON,
parsedData,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

Related

Sum numbers in base 32 or 36

I'd like to sum numbers that range from small (1) to very large (2**200). For that, I thought of using base-32 or base-36, such that the largest number would occupy a reasonable string (2**200 becomes bnklg118cog0000000000000000000000000000 in base-36).
However, how can I sum 2**200 with 1, for example? If I convert to decimal, it'll do 1.6069380442589901e+60 + 1 and the 1 will be ignored due to the precision. What I'd like is to do, for example:
bnklg118cog0000000000000000000000000000 + 1 = bnklg118cog0000000000000000000000000001
I know I can convert a number to base 36 using .toString(36), but summing them would sum strings, not giving the correct result.
Is it possible to do something like in hexadecimal, as (0xee + 0x11).toString(16) = "ff"?
Using intermediate BigInt (suggested by #robertklep), I came up with this solution: (I use here a conversion function from here to avoid problems with very large numbers)
/* Convert `value` given in a basis `radix` to BigInt
* (from https://stackoverflow.com/a/55646905/3142385)
* #param {string} value Number written in a given in basis `radix`
* #param {int} radix Basis of the number `value`
* #returns {BigInt} Converted BigInt number
*/
function ConvertToBigInt(value, radix) {
return [...value.toString()]
.reduce((r, v) => r * BigInt(radix) + BigInt(parseInt(v, radix)), 0n);
}
/* Sum two values in base `radix` and return new value on the same basis
* #param {string} value1 First value in base `radix` to be summed
* #param {string} value2 Second value in base `radix` to be summed
* #param {int} radix Basis of `value1` and `value2`
* #returns {string} Summed values in base `radix`
*/
function Sum(value1,value2, radix) {
return (ConvertToBigInt(value1.toString(),radix)+ConvertToBigInt(value2.toString(),radix)).toString(radix);
}
console.log(BigInt(2**200).toString(36),BigInt(2**199).toString(36),Sum("bnklg118comha6gqury14067gur54n8won6guf4","5tsaq0im6cb8n38dfdz0k033qfdkkbmgcbl8f7k",36))
// Output: "bnklg118comha6gqury14067gur54n8won6guf4" "5tsaq0im6cb8n38dfdz0k033qfdkkbmgcbl8f7k" "hhcw61juj0xpx9p4a5x1o09b7a4poyvd0yrp9mo"

Can I use variables in JS `e` numbers?

Per 'e' in javascript numbers I would like to use 'e' notation:
Right now I'm doing:
const USD_DECIMALS = 2
const USD = 1e2
As you can see I'm duplicating the number 2. When I'd like to do is something like:
const USD_DECIMALS = 2
const USD = 1e[USD_DECIMALS]
Is this possible? Maybe using the Number() constructor or similar?
You could make a string out of "1e2" and then run it through Number.
const USD_DECIMALS = 2
const USDString = `1e${USD_DECIMALS}`;
const USD = Number(USDString);
console.log(USD);
Not directly (without building a dynamic string, but then that's not direct either), but since 1e2 means 1 times 10², you can use either 1 * Math.pow(10, USD_DECIMALS) OR 1 * 10**USD_DECIMALS to do the same thing dynamically:
const USD_DECIMALS = 2;
const USD = 1 * 10**USD_DECIMALS;
// Or: const USD = 1 * Math.pow(10, USD_DECIMALS);
Live Example:
console.log("Various ways to get 10²:");
console.log(1e2);
const USD_DECIMALS = 2;
console.log(1 * Math.pow(10, USD_DECIMALS));
console.log(1 * 10**USD_DECIMALS);
console.log("Just for completeness, 10³:");
console.log(1e3);
const USD_DECIMALS3 = 3;
console.log(1 * Math.pow(10, USD_DECIMALS3));
console.log(1 * 10**USD_DECIMALS3);
.as-console-wrapper {
max-height: 100% !important;
}
(Side note: you sometimes need () around ** expressions where you may not expect to, such as in -1 ** 10, but not in the above.)
Just being a bit silly: you could add an e method to numbers. It's never a good idea to augment native prototypes in library code, but it can be okay in app-specific or page-specific code that doesn't have to integrate with unknown other code like libraries do, provided you do it correctly (make the extension non-enumerable). So FWIW:
Object.defineProperty(Number.prototype, "e", {
value(exp) {
return this * 10**exp;
},
// The usual set of flags for methods on built-in prototypes
// (`enumerable: false` is the default, but including it here
// just for emphasis)
writable: true,
configurable: true,
enumerable: false,
});
const USD_DECIMALS = 2;
console.log(1..e(USD_DECIMALS));
console.log((1).e(USD_DECIMALS));
let value = 1; // or whatever
console.log(value.e(USD_DECIMALS));
I probably wouldn't do it for this. Partially because I lean toward leaving native prototypes alone even in app-/page-specific code, and also because of the awkwardness of calling methods on numeric literals. Notice how I had to use 1..e(USD_DECIMALS) or (1).e(USD_DECIMALS) instead of just 1.e(USD_DECIMALS). That's because the . after a series of digits is part of the numeric literal, so it doesn't start a property/method access. The extra . solves that, or wrapping the literal in (). There's no problem when using a variable for the base value, since that's not a numeric literal, so value.e(USD_DECIMALS) works. So on the whole, for me, not worth it, but a bit of fun to look at.
The NeM is for the order of magnitude (N*10^M). What you can do is just
n=n*(10**m)
If you actually want to control the number of decimals you have to round.

How to generate random text when button is clicked? [duplicate]

I want a 5 character string composed of characters picked randomly from the set [a-zA-Z0-9].
What's the best way to do this with JavaScript?
I think this will work for you:
function makeid(length) {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let counter = 0;
while (counter < length) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}
return result;
}
console.log(makeid(5));
//Can change 7 to 2 for longer results.
let r = (Math.random() + 1).toString(36).substring(7);
console.log("random", r);
Note: The above algorithm has the following weaknesses:
It will generate anywhere between 0 and 6 characters due to the fact that trailing zeros get removed when stringifying floating points.
It depends deeply on the algorithm used to stringify floating point numbers, which is horrifically complex. (See the paper "How to Print Floating-Point Numbers Accurately".)
Math.random() may produce predictable ("random-looking" but not really random) output depending on the implementation. The resulting string is not suitable when you need to guarantee uniqueness or unpredictability.
Even if it produced 6 uniformly random, unpredictable characters, you can expect to see a duplicate after generating only about 50,000 strings, due to the birthday paradox. (sqrt(36^6) = 46656)
Math.random is bad for this kind of thing
server side
Use node crypto module -
var crypto = require("crypto");
var id = crypto.randomBytes(20).toString('hex');
// "bb5dc8842ca31d4603d6aa11448d1654"
The resulting string will be twice as long as the random bytes you generate; each byte encoded to hex is 2 characters. 20 bytes will be 40 characters of hex.
client side
Use the browser's crypto module, crypto.getRandomValues -
The crypto.getRandomValues() method lets you get cryptographically strong random values. The array given as the parameter is filled with random numbers (random in its cryptographic meaning).
// dec2hex :: Integer -> String
// i.e. 0-255 -> '00'-'ff'
function dec2hex (dec) {
return dec.toString(16).padStart(2, "0")
}
// generateId :: Integer -> String
function generateId (len) {
var arr = new Uint8Array((len || 40) / 2)
window.crypto.getRandomValues(arr)
return Array.from(arr, dec2hex).join('')
}
console.log(generateId())
// "82defcf324571e70b0521d79cce2bf3fffccd69"
console.log(generateId(20))
// "c1a050a4cd1556948d41"
A step-by-step console example -
> var arr = new Uint8Array(4) # make array of 4 bytes (values 0-255)
> arr
Uint8Array(4) [ 0, 0, 0, 0 ]
> window.crypto
Crypto { subtle: SubtleCrypto }
> window.crypto.getRandomValues()
TypeError: Crypto.getRandomValues requires at least 1 argument, but only 0 were passed
> window.crypto.getRandomValues(arr)
Uint8Array(4) [ 235, 229, 94, 228 ]
For IE11 support you can use -
(window.crypto || window.msCrypto).getRandomValues(arr)
For browser coverage see https://caniuse.com/#feat=getrandomvalues
client side (old browsers)
If you must support old browsers, consider something like uuid -
const uuid = require("uuid");
const id = uuid.v4();
// "110ec58a-a0f2-4ac4-8393-c866d813b8d1"
Short, easy and reliable
Returns exactly 5 random characters, as opposed to some of the top rated answers found here.
Math.random().toString(36).slice(2, 7);
Here's an improvement on doubletap's excellent answer. The original has two drawbacks which are addressed here:
First, as others have mentioned, it has a small probability of producing short strings or even an empty string (if the random number is 0), which may break your application. Here is a solution:
(Math.random().toString(36)+'00000000000000000').slice(2, N+2)
Second, both the original and the solution above limit the string size N to 16 characters. The following will return a string of size N for any N (but note that using N > 16 will not increase the randomness or decrease the probability of collisions):
Array(N+1).join((Math.random().toString(36)+'00000000000000000').slice(2, 18)).slice(0, N)
Explanation:
Pick a random number in the range [0,1), i.e. between 0 (inclusive) and 1 (exclusive).
Convert the number to a base-36 string, i.e. using characters 0-9 and a-z.
Pad with zeros (solves the first issue).
Slice off the leading '0.' prefix and extra padding zeros.
Repeat the string enough times to have at least N characters in it (by Joining empty strings with the shorter random string used as the delimiter).
Slice exactly N characters from the string.
Further thoughts:
This solution does not use uppercase letters, but in almost all cases (no pun intended) it does not matter.
The maximum string length at N = 16 in the original answer is measured in Chrome. In Firefox it's N = 11. But as explained, the second solution is about supporting any requested string length, not about adding randomness, so it doesn't make much of a difference.
All returned strings have an equal probability of being returned, at least as far as the results returned by Math.random() are evenly distributed (this is not cryptographic-strength randomness, in any case).
Not all possible strings of size N may be returned. In the second solution this is obvious (since the smaller string is simply being duplicated), but also in the original answer this is true since in the conversion to base-36 the last few bits may not be part of the original random bits. Specifically, if you look at the result of Math.random().toString(36), you'll notice the last character is not evenly distributed. Again, in almost all cases it does not matter, but we slice the final string from the beginning rather than the end of the random string so that short strings (e.g. N=1) aren't affected.
Update:
Here are a couple other functional-style one-liners I came up with. They differ from the solution above in that:
They use an explicit arbitrary alphabet (more generic, and suitable to the original question which asked for both uppercase and lowercase letters).
All strings of length N have an equal probability of being returned (i.e. strings contain no repetitions).
They are based on a map function, rather than the toString(36) trick, which makes them more straightforward and easy to understand.
So, say your alphabet of choice is
var s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Then these two are equivalent to each other, so you can pick whichever is more intuitive to you:
Array(N).join().split(',').map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('');
and
Array.apply(null, Array(N)).map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('');
Edit:
I seems like qubyte and Martijn de Milliano came up with solutions similar to the latter (kudos!), which I somehow missed. Since they don't look as short at a glance, I'll leave it here anyway in case someone really wants a one-liner :-)
Also, replaced 'new Array' with 'Array' in all solutions to shave off a few more bytes.
The most compact solution, because slice is shorter than substring. Subtracting from the end of the string allows to avoid floating point symbol generated by the random function:
Math.random().toString(36).slice(-5);
or even
(+new Date).toString(36).slice(-5);
Update: Added one more approach using btoa method:
btoa(Math.random()).slice(0, 5);
btoa(+new Date).slice(-7, -2);
btoa(+new Date).substr(-7, 5);
// Using Math.random and Base 36:
console.log(Math.random().toString(36).slice(-5));
// Using new Date and Base 36:
console.log((+new Date).toString(36).slice(-5));
// Using Math.random and Base 64 (btoa):
console.log(btoa(Math.random()).slice(0, 5));
// Using new Date and Base 64 (btoa):
console.log(btoa(+new Date).slice(-7, -2));
console.log(btoa(+new Date).substr(-7, 5));
A newer version with es6 spread operator:
[...Array(30)].map(() => Math.random().toString(36)[2]).join('')
The 30 is an arbitrary number, you can pick any token length you want
The 36 is the maximum radix number you can pass to numeric.toString(), which means all numbers and a-z lowercase letters
The 2 is used to pick the 3rd index from the random string which looks like this: "0.mfbiohx64i", we could take any index after 0.
Something like this should work
function randomString(len, charSet) {
charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var randomString = '';
for (var i = 0; i < len; i++) {
var randomPoz = Math.floor(Math.random() * charSet.length);
randomString += charSet.substring(randomPoz,randomPoz+1);
}
return randomString;
}
Call with default charset [a-zA-Z0-9] or send in your own:
var randomValue = randomString(5);
var randomValue = randomString(5, 'PICKCHARSFROMTHISSET');
function randomstring(L) {
var s = '';
var randomchar = function() {
var n = Math.floor(Math.random() * 62);
if (n < 10) return n; //1-10
if (n < 36) return String.fromCharCode(n + 55); //A-Z
return String.fromCharCode(n + 61); //a-z
}
while (s.length < L) s += randomchar();
return s;
}
console.log(randomstring(5));
Random String Generator (Alpha-Numeric | Alpha | Numeric)
/**
* Pseudo-random string generator
* http://stackoverflow.com/a/27872144/383904
* Default: return a random alpha-numeric string
*
* #param {Integer} len Desired length
* #param {String} an Optional (alphanumeric), "a" (alpha), "n" (numeric)
* #return {String}
*/
function randomString(len, an) {
an = an && an.toLowerCase();
var str = "",
i = 0,
min = an == "a" ? 10 : 0,
max = an == "n" ? 10 : 62;
for (; i++ < len;) {
var r = Math.random() * (max - min) + min << 0;
str += String.fromCharCode(r += r > 9 ? r < 36 ? 55 : 61 : 48);
}
return str;
}
console.log(randomString(10)); // i.e: "4Z8iNQag9v"
console.log(randomString(10, "a")); // i.e: "aUkZuHNcWw"
console.log(randomString(10, "n")); // i.e: "9055739230"
While the above uses additional checks for the desired A/N, A, N output,
let's break it down the to the essentials (Alpha-Numeric only) for a better understanding:
Create a function that accepts an argument (desired length of the random String result)
Create an empty string like var str = ""; to concatenate random characters
Inside a loop create a rand index number from 0 to 61 (0..9+A..Z+a..z = 62)
Create a conditional logic to Adjust/fix rand (since it's 0..61) incrementing it by some number (see examples below) to get back the right CharCode number and the related Character.
Inside the loop concatenate to str a String.fromCharCode( incremented rand )
Let's picture the ASCII Character table ranges:
_____0....9______A..........Z______a..........z___________ Character
| 10 | | 26 | | 26 | Tot = 62 characters
48....57 65..........90 97..........122 CharCode ranges
Math.floor( Math.random * 62 ) gives a range from 0..61 (what we need).
Let's fix the random to get the correct charCode ranges:
| rand | charCode | (0..61)rand += fix = charCode ranges |
------+----------+----------+--------------------------------+-----------------+
0..9 | 0..9 | 48..57 | rand += 48 = 48..57 |
A..Z | 10..35 | 65..90 | rand += 55 /* 90-35 = 55 */ = 65..90 |
a..z | 36..61 | 97..122 | rand += 61 /* 122-61 = 61 */ = 97..122 |
The conditional operation logic from the table above:
rand += rand>9 ? ( rand<36 ? 55 : 61 ) : 48 ;
// rand += true ? ( true ? 55 else 61 ) else 48 ;
From the explanation above, here's the resulting alpha-numeric snippet:
function randomString(len) {
var str = ""; // String result
for (var i = 0; i < len; i++) { // Loop `len` times
var rand = Math.floor(Math.random() * 62); // random: 0..61
var charCode = rand += rand > 9 ? (rand < 36 ? 55 : 61) : 48; // Get correct charCode
str += String.fromCharCode(charCode); // add Character to str
}
return str; // After all loops are done, return the concatenated string
}
console.log(randomString(10)); // i.e: "7GL9F0ne6t"
Or if you will:
const randomString = (n, r='') => {
while (n--) r += String.fromCharCode((r=Math.random()*62|0, r+=r>9?(r<36?55:61):48));
return r;
};
console.log(randomString(10))
To meet requirement [a-zA-Z0-9] and length of 5 characters, use
For Browser:
btoa(Math.random().toString()).substring(10,15);
For NodeJS:
Buffer.from(Math.random().toString()).toString("base64").substring(10,15);
Lowercase letters, uppercase letters, and numbers will occur.
(it's typescript compatible)
The simplest way is:
(new Date%9e6).toString(36)
This generate random strings of 5 characters based on the current time. Example output is 4mtxj or 4mv90 or 4mwp1
The problem with this is that if you call it two times on the same second, it will generate the same string.
The safer way is:
(0|Math.random()*9e6).toString(36)
This will generate a random string of 4 or 5 characters, always diferent. Example output is like 30jzm or 1r591 or 4su1a
In both ways the first part generate a random number. The .toString(36) part cast the number to a base36 (alphadecimal) representation of it.
Here are some easy one liners. Change new Array(5) to set the length.
Including 0-9a-z
new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36);})
Including 0-9a-zA-Z
new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36)[Math.random()<.5?"toString":"toUpperCase"]();});
Codegolfed for ES6 (0-9a-z)
Array(5).fill().map(n=>(Math.random()*36|0).toString(36)).join('')
I know everyone has got it right already, but i felt like having a go at this one in the most lightweight way possible(light on code, not CPU):
function rand(length, current) {
current = current ? current : '';
return length ? rand(--length, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".charAt(Math.floor(Math.random() * 60)) + current) : current;
}
console.log(rand(5));
It takes a bit of time to wrap your head around, but I think it really shows how awesome javascript's syntax is.
Generate a secure random alphanumeric Base-62 string:
function generateUID(length)
{
return window.btoa(String.fromCharCode(...window.crypto.getRandomValues(new Uint8Array(length * 2)))).replace(/[+/]/g, "").substring(0, length);
}
console.log(generateUID(22)); // "yFg3Upv2cE9cKOXd7hHwWp"
console.log(generateUID(5)); // "YQGzP"
There is no best way to do this. You can do it any way you prefer, as long as the result suits your requirements. To illustrate, I've created many different examples, all which should provide the same end-result
Most other answers on this page ignore the upper-case character requirement.
Here is my fastest solution and most readable. It basically does the same as the accepted solution, except it is a bit faster.
function readableRandomStringMaker(length) {
for (var s=''; s.length < length; s += 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.charAt(Math.random()*62|0));
return s;
}
console.log(readableRandomStringMaker(length));
// e3cbN
Here is a compact, recursive version which is much less readable:
const compactRandomStringMaker = (length) => length-- && "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0) + (compactRandomStringMaker(length)||"");
console.log(compactRandomStringMaker(5));
// DVudj
A more compact one-liner:
Array(5).fill().map(()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62)).join("")
// 12oEZ
A variation of the above:
" ".replaceAll(" ",()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62))
The most compact one-liner, but inefficient and unreadable - it adds random characters and removes illegal characters until length is l:
((l,f=(p='')=>p.length<l?f(p+String.fromCharCode(Math.random()*123).replace(/[^a-z0-9]/i,'')):p)=>f())(5)
A cryptographically secure version, which is wasting entropy for compactness, and is a waste regardless because the generated string is so short:
[...crypto.getRandomValues(new Uint8Array(999))].map((c)=>String.fromCharCode(c).replace(/[^a-z0-9]/i,'')).join("").substr(0,5)
// 8fzPq
Or, without the length-argument it is even shorter:
((f=(p='')=>p.length<5?f(p+String.fromCharCode(Math.random()*123).replace(/[^a-z0-9]/i,'')):p)=>f())()
// EV6c9
Then a bit more challenging - using a nameless recursive arrow function:
((l,s=((l)=>l--&&"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0)+(s(l)||""))) => s(l))(5);
// qzal4
This is a "magic" variable which provides a random character every time you access it:
const c = new class { [Symbol.toPrimitive]() { return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0) } };
console.log(c+c+c+c+c);
// AgMnz
A simpler variant of the above:
const c=()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0);
c()+c()+c()+c()+c();
// 6Qadw
In case anyone is interested in a one-liner (although not formatted as such for your convenience) that allocates the memory at once (but note that for small strings it really does not matter) here is how to do it:
Array.apply(0, Array(5)).map(function() {
return (function(charset){
return charset.charAt(Math.floor(Math.random() * charset.length))
}('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));
}).join('')
You can replace 5 by the length of the string you want. Thanks to #AriyaHidayat in this post for the solution to the map function not working on the sparse array created by Array(5).
If you are using Lodash or Underscore, then it so simple:
var randomVal = _.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 5).join('');
const c = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
const s = [...Array(5)].map(_ => c[~~(Math.random()*c.length)]).join('')
Here's the method I created.
It will create a string containing both uppercase and lowercase characters.
In addition I've included the function that will created an alphanumeric string too.
Working examples:
http://jsfiddle.net/greatbigmassive/vhsxs/ (alpha only)
http://jsfiddle.net/greatbigmassive/PJwg8/ (alphanumeric)
function randString(x){
var s = "";
while(s.length<x&&x>0){
var r = Math.random();
s+= String.fromCharCode(Math.floor(r*26) + (r>0.5?97:65));
}
return s;
}
Upgrade July 2015
This does the same thing but makes more sense and includes all letters.
var s = "";
while(s.length<x&&x>0){
v = Math.random()<0.5?32:0;
s += String.fromCharCode(Math.round(Math.random()*((122-v)-(97-v))+(97-v)));
}
One liner:
Array(15).fill(null).map(() => Math.random().toString(36).substr(2)).join('')
// Outputs: 0h61cbpw96y83qtnunwme5lxk1i70a6o5r5lckfcyh1dl9fffydcfxddd69ada9tu9jvqdx864xj1ul3wtfztmh2oz2vs3mv6ej0fe58ho1cftkjcuyl2lfkmxlwua83ibotxqc4guyuvrvtf60naob26t6swzpil
Improved #Andrew's answer above :
Array.from({ length : 1 }, () => Math.random().toString(36)[2]).join('');
Base 36 conversion of the random number is inconsistent, so selecting a single indice fixes that. You can change the length for a string with the exact length desired.
Assuming you use underscorejs it's possible to elegantly generate random string in just two lines:
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var random = _.sample(possible, 5).join('');
function randomString (strLength, charSet) {
var result = [];
strLength = strLength || 5;
charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
while (strLength--) { // (note, fixed typo)
result.push(charSet.charAt(Math.floor(Math.random() * charSet.length)));
}
return result.join('');
}
This is as clean as it will get. It is fast too, http://jsperf.com/ay-random-string.
Fast and improved algorithm. Does not guarantee uniform (see comments).
function getRandomId(length) {
if (!length) {
return '';
}
const possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let array;
if ('Uint8Array' in self && 'crypto' in self && length <= 65536) {
array = new Uint8Array(length);
self.crypto.getRandomValues(array);
} else {
array = new Array(length);
for (let i = 0; i < length; i++) {
array[i] = Math.floor(Math.random() * 62);
}
}
let result = '';
for (let i = 0; i < length; i++) {
result += possible.charAt(array[i] % 62);
}
return result;
}
How about this compact little trick?
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var stringLength = 5;
function pickRandom() {
return possible[Math.floor(Math.random() * possible.length)];
}
var randomString = Array.apply(null, Array(stringLength)).map(pickRandom).join('');
You need the Array.apply there to trick the empty array into being an array of undefineds.
If you're coding for ES2015, then building the array is a little simpler:
var randomString = Array.from({ length: stringLength }, pickRandom).join('');
You can loop through an array of items and recursively add them to a string variable, for instance if you wanted a random DNA sequence:
function randomDNA(len) {
len = len || 100
var nuc = new Array("A", "T", "C", "G")
var i = 0
var n = 0
s = ''
while (i <= len - 1) {
n = Math.floor(Math.random() * 4)
s += nuc[n]
i++
}
return s
}
console.log(randomDNA(5));
Case Insensitive Alphanumeric Chars:
function randStr(len) {
let s = '';
while (s.length < len) s += Math.random().toString(36).substr(2, len - s.length);
return s;
}
// usage
console.log(randStr(50));
The benefit of this function is that you can get different length random string and it ensures the length of the string.
Case Sensitive All Chars:
function randStr(len) {
let s = '';
while (len--) s += String.fromCodePoint(Math.floor(Math.random() * (126 - 33) + 33));
return s;
}
// usage
console.log(randStr(50));
Custom Chars
function randStr(len, chars='abc123') {
let s = '';
while (len--) s += chars[Math.floor(Math.random() * chars.length)];
return s;
}
// usage
console.log(randStr(50));
console.log(randStr(50, 'abc'));
console.log(randStr(50, 'aab')); // more a than b
The problem with responses to "I need random strings" questions (in whatever language) is practically every solution uses a flawed primary specification of string length. The questions themselves rarely reveal why the random strings are needed, but I would challenge you rarely need random strings of length, say 8. What you invariably need is some number of unique strings, for example, to use as identifiers for some purpose.
There are two leading ways to get strictly unique strings: deterministically (which is not random) and store/compare (which is onerous). What do we do? We give up the ghost. We go with probabilistic uniqueness instead. That is, we accept that there is some (however small) risk that our strings won't be unique. This is where understanding collision probability and entropy are helpful.
So I'll rephrase the invariable need as needing some number of strings with a small risk of repeat. As a concrete example, let's say you want to generate a potential of 5 million IDs. You don't want to store and compare each new string, and you want them to be random, so you accept some risk of repeat. As example, let's say a risk of less than 1 in a trillion chance of repeat. So what length of string do you need? Well, that question is underspecified as it depends on the characters used. But more importantly, it's misguided. What you need is a specification of the entropy of the strings, not their length. Entropy can be directly related to the probability of a repeat in some number of strings. String length can't.
And this is where a library like EntropyString can help. To generate random IDs that have less than 1 in a trillion chance of repeat in 5 million strings using entropy-string:
import {Random, Entropy} from 'entropy-string'
const random = new Random()
const bits = Entropy.bits(5e6, 1e12)
const string = random.string(bits)
"44hTNghjNHGGRHqH9"
entropy-string uses a character set with 32 characters by default. There are other predefined characters sets, and you can specify your own characters as well. For example, generating IDs with the same entropy as above but using hex characters:
import {Random, Entropy, charSet16} from './entropy-string'
const random = new Random(charSet16)
const bits = Entropy.bits(5e6, 1e12)
const string = random.string(bits)
"27b33372ade513715481f"
Note the difference in string length due to the difference in total number of characters in the character set used. The risk of repeat in the specified number of potential strings is the same. The string lengths are not. And best of all, the risk of repeat and the potential number of strings is explicit. No more guessing with string length.
One-liner using map that gives you full control on the length and characters.
const rnd = (len, chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') => [...Array(len)].map(() => chars.charAt(Math.floor(Math.random() * chars.length))).join('')
console.log(rnd(12))

Is it possible to check function arguments by google closure linter (gjslint) tool?

For example, I have the following script "test.js":
/**
* Adds two numbers.
* #param {number} a First number.
* #param {number} b First number.
* #return {number} Sum of two numbers.
*/
function add(a, b) {
return a + b;
}
var sum = add(1, 2);
var sum1 = add(1, 2, 4);
var sum2 = add('1', '2');
The function "add" is annotated by jsdoc. It accepts two numbers.
I would like to check the input parameters by gjslint.
gjslint --strict --jslint_error "all" --jsdoc test.js
I expect to see 2 errors for lines where sum1 (3 input parameters) and sum2 (2 string parameters) are calculated. But the tools says "1 files checked, no errors found."
I have not used the Closure Linter, but I believe that is more used for style issues - missing semicolons and such (adherence to http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
The Closure Compiler, however, will give a JSC_WRONG_ARGUMENT_COUNT warning for the use case above.

Generate random string/characters in JavaScript

I want a 5 character string composed of characters picked randomly from the set [a-zA-Z0-9].
What's the best way to do this with JavaScript?
I think this will work for you:
function makeid(length) {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let counter = 0;
while (counter < length) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
counter += 1;
}
return result;
}
console.log(makeid(5));
//Can change 7 to 2 for longer results.
let r = (Math.random() + 1).toString(36).substring(7);
console.log("random", r);
Note: The above algorithm has the following weaknesses:
It will generate anywhere between 0 and 6 characters due to the fact that trailing zeros get removed when stringifying floating points.
It depends deeply on the algorithm used to stringify floating point numbers, which is horrifically complex. (See the paper "How to Print Floating-Point Numbers Accurately".)
Math.random() may produce predictable ("random-looking" but not really random) output depending on the implementation. The resulting string is not suitable when you need to guarantee uniqueness or unpredictability.
Even if it produced 6 uniformly random, unpredictable characters, you can expect to see a duplicate after generating only about 50,000 strings, due to the birthday paradox. (sqrt(36^6) = 46656)
Math.random is bad for this kind of thing
server side
Use node crypto module -
var crypto = require("crypto");
var id = crypto.randomBytes(20).toString('hex');
// "bb5dc8842ca31d4603d6aa11448d1654"
The resulting string will be twice as long as the random bytes you generate; each byte encoded to hex is 2 characters. 20 bytes will be 40 characters of hex.
client side
Use the browser's crypto module, crypto.getRandomValues -
The crypto.getRandomValues() method lets you get cryptographically strong random values. The array given as the parameter is filled with random numbers (random in its cryptographic meaning).
// dec2hex :: Integer -> String
// i.e. 0-255 -> '00'-'ff'
function dec2hex (dec) {
return dec.toString(16).padStart(2, "0")
}
// generateId :: Integer -> String
function generateId (len) {
var arr = new Uint8Array((len || 40) / 2)
window.crypto.getRandomValues(arr)
return Array.from(arr, dec2hex).join('')
}
console.log(generateId())
// "82defcf324571e70b0521d79cce2bf3fffccd69"
console.log(generateId(20))
// "c1a050a4cd1556948d41"
A step-by-step console example -
> var arr = new Uint8Array(4) # make array of 4 bytes (values 0-255)
> arr
Uint8Array(4) [ 0, 0, 0, 0 ]
> window.crypto
Crypto { subtle: SubtleCrypto }
> window.crypto.getRandomValues()
TypeError: Crypto.getRandomValues requires at least 1 argument, but only 0 were passed
> window.crypto.getRandomValues(arr)
Uint8Array(4) [ 235, 229, 94, 228 ]
For IE11 support you can use -
(window.crypto || window.msCrypto).getRandomValues(arr)
For browser coverage see https://caniuse.com/#feat=getrandomvalues
client side (old browsers)
If you must support old browsers, consider something like uuid -
const uuid = require("uuid");
const id = uuid.v4();
// "110ec58a-a0f2-4ac4-8393-c866d813b8d1"
Short, easy and reliable
Returns exactly 5 random characters, as opposed to some of the top rated answers found here.
Math.random().toString(36).slice(2, 7);
Here's an improvement on doubletap's excellent answer. The original has two drawbacks which are addressed here:
First, as others have mentioned, it has a small probability of producing short strings or even an empty string (if the random number is 0), which may break your application. Here is a solution:
(Math.random().toString(36)+'00000000000000000').slice(2, N+2)
Second, both the original and the solution above limit the string size N to 16 characters. The following will return a string of size N for any N (but note that using N > 16 will not increase the randomness or decrease the probability of collisions):
Array(N+1).join((Math.random().toString(36)+'00000000000000000').slice(2, 18)).slice(0, N)
Explanation:
Pick a random number in the range [0,1), i.e. between 0 (inclusive) and 1 (exclusive).
Convert the number to a base-36 string, i.e. using characters 0-9 and a-z.
Pad with zeros (solves the first issue).
Slice off the leading '0.' prefix and extra padding zeros.
Repeat the string enough times to have at least N characters in it (by Joining empty strings with the shorter random string used as the delimiter).
Slice exactly N characters from the string.
Further thoughts:
This solution does not use uppercase letters, but in almost all cases (no pun intended) it does not matter.
The maximum string length at N = 16 in the original answer is measured in Chrome. In Firefox it's N = 11. But as explained, the second solution is about supporting any requested string length, not about adding randomness, so it doesn't make much of a difference.
All returned strings have an equal probability of being returned, at least as far as the results returned by Math.random() are evenly distributed (this is not cryptographic-strength randomness, in any case).
Not all possible strings of size N may be returned. In the second solution this is obvious (since the smaller string is simply being duplicated), but also in the original answer this is true since in the conversion to base-36 the last few bits may not be part of the original random bits. Specifically, if you look at the result of Math.random().toString(36), you'll notice the last character is not evenly distributed. Again, in almost all cases it does not matter, but we slice the final string from the beginning rather than the end of the random string so that short strings (e.g. N=1) aren't affected.
Update:
Here are a couple other functional-style one-liners I came up with. They differ from the solution above in that:
They use an explicit arbitrary alphabet (more generic, and suitable to the original question which asked for both uppercase and lowercase letters).
All strings of length N have an equal probability of being returned (i.e. strings contain no repetitions).
They are based on a map function, rather than the toString(36) trick, which makes them more straightforward and easy to understand.
So, say your alphabet of choice is
var s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Then these two are equivalent to each other, so you can pick whichever is more intuitive to you:
Array(N).join().split(',').map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('');
and
Array.apply(null, Array(N)).map(function() { return s.charAt(Math.floor(Math.random() * s.length)); }).join('');
Edit:
I seems like qubyte and Martijn de Milliano came up with solutions similar to the latter (kudos!), which I somehow missed. Since they don't look as short at a glance, I'll leave it here anyway in case someone really wants a one-liner :-)
Also, replaced 'new Array' with 'Array' in all solutions to shave off a few more bytes.
The most compact solution, because slice is shorter than substring. Subtracting from the end of the string allows to avoid floating point symbol generated by the random function:
Math.random().toString(36).slice(-5);
or even
(+new Date).toString(36).slice(-5);
Update: Added one more approach using btoa method:
btoa(Math.random()).slice(0, 5);
btoa(+new Date).slice(-7, -2);
btoa(+new Date).substr(-7, 5);
// Using Math.random and Base 36:
console.log(Math.random().toString(36).slice(-5));
// Using new Date and Base 36:
console.log((+new Date).toString(36).slice(-5));
// Using Math.random and Base 64 (btoa):
console.log(btoa(Math.random()).slice(0, 5));
// Using new Date and Base 64 (btoa):
console.log(btoa(+new Date).slice(-7, -2));
console.log(btoa(+new Date).substr(-7, 5));
A newer version with es6 spread operator:
[...Array(30)].map(() => Math.random().toString(36)[2]).join('')
The 30 is an arbitrary number, you can pick any token length you want
The 36 is the maximum radix number you can pass to numeric.toString(), which means all numbers and a-z lowercase letters
The 2 is used to pick the 3rd index from the random string which looks like this: "0.mfbiohx64i", we could take any index after 0.
Something like this should work
function randomString(len, charSet) {
charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var randomString = '';
for (var i = 0; i < len; i++) {
var randomPoz = Math.floor(Math.random() * charSet.length);
randomString += charSet.substring(randomPoz,randomPoz+1);
}
return randomString;
}
Call with default charset [a-zA-Z0-9] or send in your own:
var randomValue = randomString(5);
var randomValue = randomString(5, 'PICKCHARSFROMTHISSET');
function randomstring(L) {
var s = '';
var randomchar = function() {
var n = Math.floor(Math.random() * 62);
if (n < 10) return n; //1-10
if (n < 36) return String.fromCharCode(n + 55); //A-Z
return String.fromCharCode(n + 61); //a-z
}
while (s.length < L) s += randomchar();
return s;
}
console.log(randomstring(5));
Random String Generator (Alpha-Numeric | Alpha | Numeric)
/**
* Pseudo-random string generator
* http://stackoverflow.com/a/27872144/383904
* Default: return a random alpha-numeric string
*
* #param {Integer} len Desired length
* #param {String} an Optional (alphanumeric), "a" (alpha), "n" (numeric)
* #return {String}
*/
function randomString(len, an) {
an = an && an.toLowerCase();
var str = "",
i = 0,
min = an == "a" ? 10 : 0,
max = an == "n" ? 10 : 62;
for (; i++ < len;) {
var r = Math.random() * (max - min) + min << 0;
str += String.fromCharCode(r += r > 9 ? r < 36 ? 55 : 61 : 48);
}
return str;
}
console.log(randomString(10)); // i.e: "4Z8iNQag9v"
console.log(randomString(10, "a")); // i.e: "aUkZuHNcWw"
console.log(randomString(10, "n")); // i.e: "9055739230"
While the above uses additional checks for the desired A/N, A, N output,
let's break it down the to the essentials (Alpha-Numeric only) for a better understanding:
Create a function that accepts an argument (desired length of the random String result)
Create an empty string like var str = ""; to concatenate random characters
Inside a loop create a rand index number from 0 to 61 (0..9+A..Z+a..z = 62)
Create a conditional logic to Adjust/fix rand (since it's 0..61) incrementing it by some number (see examples below) to get back the right CharCode number and the related Character.
Inside the loop concatenate to str a String.fromCharCode( incremented rand )
Let's picture the ASCII Character table ranges:
_____0....9______A..........Z______a..........z___________ Character
| 10 | | 26 | | 26 | Tot = 62 characters
48....57 65..........90 97..........122 CharCode ranges
Math.floor( Math.random * 62 ) gives a range from 0..61 (what we need).
Let's fix the random to get the correct charCode ranges:
| rand | charCode | (0..61)rand += fix = charCode ranges |
------+----------+----------+--------------------------------+-----------------+
0..9 | 0..9 | 48..57 | rand += 48 = 48..57 |
A..Z | 10..35 | 65..90 | rand += 55 /* 90-35 = 55 */ = 65..90 |
a..z | 36..61 | 97..122 | rand += 61 /* 122-61 = 61 */ = 97..122 |
The conditional operation logic from the table above:
rand += rand>9 ? ( rand<36 ? 55 : 61 ) : 48 ;
// rand += true ? ( true ? 55 else 61 ) else 48 ;
From the explanation above, here's the resulting alpha-numeric snippet:
function randomString(len) {
var str = ""; // String result
for (var i = 0; i < len; i++) { // Loop `len` times
var rand = Math.floor(Math.random() * 62); // random: 0..61
var charCode = rand += rand > 9 ? (rand < 36 ? 55 : 61) : 48; // Get correct charCode
str += String.fromCharCode(charCode); // add Character to str
}
return str; // After all loops are done, return the concatenated string
}
console.log(randomString(10)); // i.e: "7GL9F0ne6t"
Or if you will:
const randomString = (n, r='') => {
while (n--) r += String.fromCharCode((r=Math.random()*62|0, r+=r>9?(r<36?55:61):48));
return r;
};
console.log(randomString(10))
To meet requirement [a-zA-Z0-9] and length of 5 characters, use
For Browser:
btoa(Math.random().toString()).substring(10,15);
For NodeJS:
Buffer.from(Math.random().toString()).toString("base64").substring(10,15);
Lowercase letters, uppercase letters, and numbers will occur.
(it's typescript compatible)
The simplest way is:
(new Date%9e6).toString(36)
This generate random strings of 5 characters based on the current time. Example output is 4mtxj or 4mv90 or 4mwp1
The problem with this is that if you call it two times on the same second, it will generate the same string.
The safer way is:
(0|Math.random()*9e6).toString(36)
This will generate a random string of 4 or 5 characters, always diferent. Example output is like 30jzm or 1r591 or 4su1a
In both ways the first part generate a random number. The .toString(36) part cast the number to a base36 (alphadecimal) representation of it.
Here are some easy one liners. Change new Array(5) to set the length.
Including 0-9a-z
new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36);})
Including 0-9a-zA-Z
new Array(5).join().replace(/(.|$)/g, function(){return ((Math.random()*36)|0).toString(36)[Math.random()<.5?"toString":"toUpperCase"]();});
Codegolfed for ES6 (0-9a-z)
Array(5).fill().map(n=>(Math.random()*36|0).toString(36)).join('')
I know everyone has got it right already, but i felt like having a go at this one in the most lightweight way possible(light on code, not CPU):
function rand(length, current) {
current = current ? current : '';
return length ? rand(--length, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".charAt(Math.floor(Math.random() * 60)) + current) : current;
}
console.log(rand(5));
It takes a bit of time to wrap your head around, but I think it really shows how awesome javascript's syntax is.
Generate a secure random alphanumeric Base-62 string:
function generateUID(length)
{
return window.btoa(String.fromCharCode(...window.crypto.getRandomValues(new Uint8Array(length * 2)))).replace(/[+/]/g, "").substring(0, length);
}
console.log(generateUID(22)); // "yFg3Upv2cE9cKOXd7hHwWp"
console.log(generateUID(5)); // "YQGzP"
There is no best way to do this. You can do it any way you prefer, as long as the result suits your requirements. To illustrate, I've created many different examples, all which should provide the same end-result
Most other answers on this page ignore the upper-case character requirement.
Here is my fastest solution and most readable. It basically does the same as the accepted solution, except it is a bit faster.
function readableRandomStringMaker(length) {
for (var s=''; s.length < length; s += 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.charAt(Math.random()*62|0));
return s;
}
console.log(readableRandomStringMaker(length));
// e3cbN
Here is a compact, recursive version which is much less readable:
const compactRandomStringMaker = (length) => length-- && "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0) + (compactRandomStringMaker(length)||"");
console.log(compactRandomStringMaker(5));
// DVudj
A more compact one-liner:
Array(5).fill().map(()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62)).join("")
// 12oEZ
A variation of the above:
" ".replaceAll(" ",()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62))
The most compact one-liner, but inefficient and unreadable - it adds random characters and removes illegal characters until length is l:
((l,f=(p='')=>p.length<l?f(p+String.fromCharCode(Math.random()*123).replace(/[^a-z0-9]/i,'')):p)=>f())(5)
A cryptographically secure version, which is wasting entropy for compactness, and is a waste regardless because the generated string is so short:
[...crypto.getRandomValues(new Uint8Array(999))].map((c)=>String.fromCharCode(c).replace(/[^a-z0-9]/i,'')).join("").substr(0,5)
// 8fzPq
Or, without the length-argument it is even shorter:
((f=(p='')=>p.length<5?f(p+String.fromCharCode(Math.random()*123).replace(/[^a-z0-9]/i,'')):p)=>f())()
// EV6c9
Then a bit more challenging - using a nameless recursive arrow function:
((l,s=((l)=>l--&&"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0)+(s(l)||""))) => s(l))(5);
// qzal4
This is a "magic" variable which provides a random character every time you access it:
const c = new class { [Symbol.toPrimitive]() { return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0) } };
console.log(c+c+c+c+c);
// AgMnz
A simpler variant of the above:
const c=()=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(Math.random()*62|0);
c()+c()+c()+c()+c();
// 6Qadw
In case anyone is interested in a one-liner (although not formatted as such for your convenience) that allocates the memory at once (but note that for small strings it really does not matter) here is how to do it:
Array.apply(0, Array(5)).map(function() {
return (function(charset){
return charset.charAt(Math.floor(Math.random() * charset.length))
}('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));
}).join('')
You can replace 5 by the length of the string you want. Thanks to #AriyaHidayat in this post for the solution to the map function not working on the sparse array created by Array(5).
If you are using Lodash or Underscore, then it so simple:
var randomVal = _.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 5).join('');
const c = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
const s = [...Array(5)].map(_ => c[~~(Math.random()*c.length)]).join('')
Here's the method I created.
It will create a string containing both uppercase and lowercase characters.
In addition I've included the function that will created an alphanumeric string too.
Working examples:
http://jsfiddle.net/greatbigmassive/vhsxs/ (alpha only)
http://jsfiddle.net/greatbigmassive/PJwg8/ (alphanumeric)
function randString(x){
var s = "";
while(s.length<x&&x>0){
var r = Math.random();
s+= String.fromCharCode(Math.floor(r*26) + (r>0.5?97:65));
}
return s;
}
Upgrade July 2015
This does the same thing but makes more sense and includes all letters.
var s = "";
while(s.length<x&&x>0){
v = Math.random()<0.5?32:0;
s += String.fromCharCode(Math.round(Math.random()*((122-v)-(97-v))+(97-v)));
}
One liner:
Array(15).fill(null).map(() => Math.random().toString(36).substr(2)).join('')
// Outputs: 0h61cbpw96y83qtnunwme5lxk1i70a6o5r5lckfcyh1dl9fffydcfxddd69ada9tu9jvqdx864xj1ul3wtfztmh2oz2vs3mv6ej0fe58ho1cftkjcuyl2lfkmxlwua83ibotxqc4guyuvrvtf60naob26t6swzpil
Improved #Andrew's answer above :
Array.from({ length : 1 }, () => Math.random().toString(36)[2]).join('');
Base 36 conversion of the random number is inconsistent, so selecting a single indice fixes that. You can change the length for a string with the exact length desired.
Assuming you use underscorejs it's possible to elegantly generate random string in just two lines:
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var random = _.sample(possible, 5).join('');
function randomString (strLength, charSet) {
var result = [];
strLength = strLength || 5;
charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
while (strLength--) { // (note, fixed typo)
result.push(charSet.charAt(Math.floor(Math.random() * charSet.length)));
}
return result.join('');
}
This is as clean as it will get. It is fast too, http://jsperf.com/ay-random-string.
Fast and improved algorithm. Does not guarantee uniform (see comments).
function getRandomId(length) {
if (!length) {
return '';
}
const possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let array;
if ('Uint8Array' in self && 'crypto' in self && length <= 65536) {
array = new Uint8Array(length);
self.crypto.getRandomValues(array);
} else {
array = new Array(length);
for (let i = 0; i < length; i++) {
array[i] = Math.floor(Math.random() * 62);
}
}
let result = '';
for (let i = 0; i < length; i++) {
result += possible.charAt(array[i] % 62);
}
return result;
}
How about this compact little trick?
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var stringLength = 5;
function pickRandom() {
return possible[Math.floor(Math.random() * possible.length)];
}
var randomString = Array.apply(null, Array(stringLength)).map(pickRandom).join('');
You need the Array.apply there to trick the empty array into being an array of undefineds.
If you're coding for ES2015, then building the array is a little simpler:
var randomString = Array.from({ length: stringLength }, pickRandom).join('');
You can loop through an array of items and recursively add them to a string variable, for instance if you wanted a random DNA sequence:
function randomDNA(len) {
len = len || 100
var nuc = new Array("A", "T", "C", "G")
var i = 0
var n = 0
s = ''
while (i <= len - 1) {
n = Math.floor(Math.random() * 4)
s += nuc[n]
i++
}
return s
}
console.log(randomDNA(5));
Case Insensitive Alphanumeric Chars:
function randStr(len) {
let s = '';
while (s.length < len) s += Math.random().toString(36).substr(2, len - s.length);
return s;
}
// usage
console.log(randStr(50));
The benefit of this function is that you can get different length random string and it ensures the length of the string.
Case Sensitive All Chars:
function randStr(len) {
let s = '';
while (len--) s += String.fromCodePoint(Math.floor(Math.random() * (126 - 33) + 33));
return s;
}
// usage
console.log(randStr(50));
Custom Chars
function randStr(len, chars='abc123') {
let s = '';
while (len--) s += chars[Math.floor(Math.random() * chars.length)];
return s;
}
// usage
console.log(randStr(50));
console.log(randStr(50, 'abc'));
console.log(randStr(50, 'aab')); // more a than b
The problem with responses to "I need random strings" questions (in whatever language) is practically every solution uses a flawed primary specification of string length. The questions themselves rarely reveal why the random strings are needed, but I would challenge you rarely need random strings of length, say 8. What you invariably need is some number of unique strings, for example, to use as identifiers for some purpose.
There are two leading ways to get strictly unique strings: deterministically (which is not random) and store/compare (which is onerous). What do we do? We give up the ghost. We go with probabilistic uniqueness instead. That is, we accept that there is some (however small) risk that our strings won't be unique. This is where understanding collision probability and entropy are helpful.
So I'll rephrase the invariable need as needing some number of strings with a small risk of repeat. As a concrete example, let's say you want to generate a potential of 5 million IDs. You don't want to store and compare each new string, and you want them to be random, so you accept some risk of repeat. As example, let's say a risk of less than 1 in a trillion chance of repeat. So what length of string do you need? Well, that question is underspecified as it depends on the characters used. But more importantly, it's misguided. What you need is a specification of the entropy of the strings, not their length. Entropy can be directly related to the probability of a repeat in some number of strings. String length can't.
And this is where a library like EntropyString can help. To generate random IDs that have less than 1 in a trillion chance of repeat in 5 million strings using entropy-string:
import {Random, Entropy} from 'entropy-string'
const random = new Random()
const bits = Entropy.bits(5e6, 1e12)
const string = random.string(bits)
"44hTNghjNHGGRHqH9"
entropy-string uses a character set with 32 characters by default. There are other predefined characters sets, and you can specify your own characters as well. For example, generating IDs with the same entropy as above but using hex characters:
import {Random, Entropy, charSet16} from './entropy-string'
const random = new Random(charSet16)
const bits = Entropy.bits(5e6, 1e12)
const string = random.string(bits)
"27b33372ade513715481f"
Note the difference in string length due to the difference in total number of characters in the character set used. The risk of repeat in the specified number of potential strings is the same. The string lengths are not. And best of all, the risk of repeat and the potential number of strings is explicit. No more guessing with string length.
One-liner using map that gives you full control on the length and characters.
const rnd = (len, chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') => [...Array(len)].map(() => chars.charAt(Math.floor(Math.random() * chars.length))).join('')
console.log(rnd(12))

Categories