Random values in JavaScript and Node.js


There are several different ways of generating random values in Node.js (JavaScript). Some methods use only base JavaScript functions (e.g. Math.random()), Others require additional modules. Generated values also differ from method to method. Some may return only numbers whereas others return a base64 string or universally unique identifiers (UUID).

Generate random number using Math.random()

The easiest way is to generate numbers using Math.random(). That function returns a floating-point, pseudo-random number that is from 0 (inclusive) up to 1 (exclusive) ([0, 1)). Most of the currently available browsers also support it if you need random numbers generation functionality in a browser.

Note that Math.random() returns pseudo-random number. The pseudo-random number appears to be random, but it is not. The random generator is seeded from the current time to increase the “randomness” of the generated numbers.

If you would like to get random numbers generated in a specific range (e.g. [low, high)) you would need to scale generated number to your desired range.

A few examples of scaling generated number to your desired range:

This function generates floating-point between two numbers low (inclusive) and high (exclusive) ([low, high))

function random(low, high) {
  return Math.random() * (high - low) + low
}

This function generates random integer between two numbers low (inclusive) and high (exclusive) ([low, high))

function randomInt(low, high) {
  return Math.floor(Math.random() * (high - low) + low)
}

This function generates random integer between two numbers low (inclusive) and high (inclusive) ([low, high])

function randomIntInc(low, high) {
  return Math.floor(Math.random() * (high - low + 1) + low)
}

Let’s create an array and populate it with random numbers from the range: [1, 10].

var numbers = new Array(10)
for (var i = 0; i < numbers.length; i++) {
  numbers[i] = randomIntInc(1, 10)
}

It will give something like:

[4, 6, 9, 9, 9, 8, 4, 1, 10, 1]

Sometimes a single digit is not enough, and you would need a number composed of the specific amount of digits. Note that generated values will be JavaScript strings, not JavaScript numbers. In that case, you can perform modifications of generated values with another function, e.g., padding function.

Below there is an example of a left padding function. You can find many more examples also on Internet e.g. pad, lpad, rpad etc. from underscore.string.

function leftPad(str, length) {
  str = str == null ? '' : String(str)
  length = ~~length
  pad = ''
  padLength = length - str.length

  while (padLength--) {
    pad += '0'
  }

  return pad + str
}

Let’s perform padding transformation with leftPad function with padding length set to 3.

var numbers = new Array(10)
for (var i = 0; i < numbers.length; i++) {
  numbers[i] = leftPad(randomIntInc(1, 10), 3)
}
['007', '006', '009', '010', '002', '005', '003', '006', '004', '009']

An alternative to padding function might be number conversion to string using a different base, e.g., base 2 for the binary representation of base 16 for hexadecimal representation.

...
numbers[i] = randomIntInc(1, 10).toString(2); // base 2 - binary format
...

A few output examples:

// output - base 2 - binary format
['10001','110','11101','11110','10','1111','100000','1000000','1110','1001']

// output - base 8 - octal format
['21','6','35','36','2','17','40','100','16','11']

// output - base 16 - hexadecimal format
['11','6','1d','1e','2','f','20','40','e','9']

Generate universally unique identifiers (UUID)

Another option to generate random values is to use universally unique identifier (UUID) version 4 for that purpose (RFC4122).

UUID is a 128-bit number. Algorithm generating UUID version 4 relies only on random or pseudo-random numbers. It sets only six bits (four version number bits to ‘0100’ and two reserved bits to ‘01’) whereas all remaining 122 bits are random / pseudo-random.

UUID version 4 have to form as follows:

xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx

where x is any hexadecimal digit and y is one of 8, 9, A, or B (e.g., efe1f2aa-1e99-40f2-83fa-8519acd8c34c).

There are several Node.js modules generating UUID numbers. The one which I use very often is uuid. Simple and fast implementation of RFC4122 (v1 and v4) UUIDs.

The uuid module is very simple to use. You can install it using npm manager:

npm install uuid

And use it in your script e.g.:

const uuidv4 = require('uuid/v4')

var uuid1 = uuidv4() // e.g. 32a4fbed-676d-47f9-a321-cb2f267e2918
var uuid2 = uuidv4() // e.g. 8b68cf5b-d619-4281-b560-1578b0ee891d

Note that this module uses Math.random() as a base function for a random number generator and therefore it can also be used as a library in your browser.

Generate random values using Node.js crypto module

An alternative to the options mentioned above might be a generation of random values using crypto.randomBytes(size, [callback]) method from crypto Node.js module. randomBytes returns a buffer with randomly generated bytes. It can work asynchronously (when the callback function is provided) or synchronously (only size parameter is provided). The returned buffer can be transformed using different encodings to get required format.

Some examples of generating random values in different string formats.

Random value in hex format

In hexadecimal format, a single byte generates two characters. So there is no point to create the same number of bytes as the number of output characters. We can only produce half of the bytes.

var crypto = require('crypto')

function randomValueHex(len) {
  return crypto
    .randomBytes(Math.ceil(len / 2))
    .toString('hex') // convert to hexadecimal format
    .slice(0, len) // return required number of characters
}

var value1 = randomValueHex(12) // value 'd5be8583137b'
var value2 = randomValueHex(2) // value 'd9'
var value3 = randomValueHex(7) // value 'ad0fc8c'

Random values in base64 format

In Base64 format, the number of output bytes per input byte is 4/3 (33% overhead). So to get X output characters, we need to generate 3/4 of X bytes. A string in base64 format is composed of the characters as follows: a-z, A-Z, 0-9, + and /. Sometimes ‘+’ and ‘/’ characters are not allowed in the output string. We can replace those two characters after conversion to base64 format.

var crypto = require('crypto')

function randomValueBase64(len) {
  return crypto
    .randomBytes(Math.ceil((len * 3) / 4))
    .toString('base64') // convert to base64 format
    .slice(0, len) // return required number of characters
    .replace(/\+/g, '0') // replace '+' with '0'
    .replace(/\//g, '0') // replace '/' with '0'
}

var value1 = randomValueBase64(12) // value 'wNm2OQu7UaTB'
var value2 = randomValueBase64(2) // value 'Lj'
var value3 = randomValueBase64(7) // value 'jWHSOzk'

Random values formatted with biguint-format module

The randomBytes function of crypto module returns a node Buffer with random bytes. A Buffer instance can be very easily converted to string (supports several formats, e.g., decimal, binary, etc.) using node biguint-format module. Note that the module can format arbitrary length unsigned integer. So you can generate numbers above limits of JavaScript IEEE 754 double-precision floats.

Some examples:

var crypto = require('crypto'),
  biguint = require('biguint-format')

function random(qty) {
  return crypto.randomBytes(qty)
}

console.log(biguint.format(random(8), 'dec'))
// result "6848583289632568793"

console.log(biguint.format(random(8), 'hex', { prefix: '0x' }))
// result "0xd8765863a5bbdc3"

console.log(biguint.format(random(16), 'hex', { prefix: '0x' }))
// result "0x684c3d299c9f15573ea9b93987ca4400"

console.log(biguint.format(random(6), 'bin', { groupsize: 4 }))
// result "1001 1110 1001 0010 0000 0111 0011 1000 1000 1110 0111 0011"

You can read more about biguint-format module on its wiki page.

Random values from a limited set of characters

Sometimes there is a need to generate a random value from a limited set of characters, e.g., only lowercase characters or odd digits (1,3,5,7,9). As in the previous example, to implement that functionality we can use node crypto module.

The function below generates a specific number of characters from a given set. If a set of characters has not been provided, it will use all numbers and lowercase and uppercase characters.

var crypto = require('crypto');

function random (howMany, chars) {
      chars = chars
        || 'abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789';
    var rnd = crypto.randomBytes(howMany)
        , value = new Array(howMany)
        , len = len = Math.min(256, chars.length),
        , d = 256 / len

    for (var i = 0; i < howMany; i++) {
          value[i] = chars[Math.floor(rnd[i] / d)]
    };

    return value.join('');
}

And the below function executions returns output like:

random(10) // returns "rkp6rt7EBc"
random(10, 'ABBB') // returns "BABBBBABAB"

Note that the function will use only the first 256 characters as numbers generated by the randomBytes function have values from 0 to 255. Also to increase the probability of a specific character in the output, you have to add more of those characters in the input set, e.g., character set as follows: ABBB will have probabilities of letters: A - 25% and B - 75%.

Conclusion

In this post, I shared with you several different methods to generate random values in Node.js. Some of them might be very good to solve a specific type of problem and entirely useless for other kinds of problems.

Every time you have a dilemma which method will work for you, think about allowed output characters (can it have only numbers or letters and numbers), change of having duplicates (e.g. 128-bit UUID values are “very unique”) and method “expandability” (e.g., UUID have defined format which can’t be changed that easily, whereas random values generated with crypto.randomBytes can be transformed into different formats). That should help you to select the “right” method for your needs.

See also


Tags:

#base64 #binary #crypto #formatting #hex #javascript #node.js #number #random #uuid


You may also be interested in:



comments powered by Disqus