Generating and managing random lists with JavaScript and Apps Script

This namespace contains a bunch of useful methods to generate random lists and strings. It's plain JavaScript so works on both Apps Script or client side JavaScript.

The main features are
  • creating random strings, arrays and grids
  • limiting used values using ranges, regular expressions and lists
  • shuffling strings and arrays
  • using seed values and phrases to create repeatable randomness
  • unshuffling previously shuffled arrays and strings using the seed values they were shuffled with

Why would you want this?

If you do any kind of random list or string creation, or you need to shuffle (and unshuffle) data both client and server side, you'll find this set of functions very useful. For example you can create random test spreadsheet data, or even use the seeded shuffling as an aid for encryption (I use this in Generating coupon codes with expiry dates)

Using

If you are using Apps Script, the cLucky library is available here 

1X-tyDMF_iILp3cQ4MMCY0GQwrOPHl8ocKtWhqVuw1u5PG5wMytL6mjOP

or you can get the source from github.  If using from the Apps Script library, then you can define Lucky as
var Lucky = cLucky.Lucky;

Examples

The list of methods and options appears at the end of this post, but the best way to demonstrate is simply to show some examples. Here's a few examples of what you can do, starting with generating arrays of integers with various options. I'll explain the show() function I'm using in these tests later. It's just a way of displaying the results. 

Lucky.get(options)

This method returns various arrays of random integers.


Get 2 arrays of length 10 populated with random numbers between  1 and 100
var options = {
  min: 1,
  max: 100,
  size: 10
};
for (var i = 0; i < 2 ; i++) {
  show('Array '+i, Lucky.get(options));
}
Array 0
[66,50,90,63,65,63,28,57,74,77]
Array 1
[35,86,86,2,92,34,4,45,22,98]

Get 2 arrays of length between 3 and 10, populated with random numbers between  1 and 5
var options = {
  min: 1,
  max: 5,
  maxSize: 10,
  size:3
};
for (var i = 0; i < 2 ; i++) {
  show('Array random size '+i, Lucky.get(options));
}
Array random size 0
[5,2,4,4,3,2,1]
Array random size 1
[3,4,2,5,5,2]

Lucky.shuffle(array[,seed])

This will shuffle an array in random order
show('Array shuffled', Lucky.shuffle([1, 2, 3, 4, 5]));
Array shuffled
[2,3,5,4,1]

Seeds

Being able to generate repeatable random number can be important for some applications, not least because if you can repeat random sequence used for shuffling, this also means you can unshuffle it if you know the seed it was shuffled with. MDN JavaScript documentation says this.

The Math.random() function returns a floating-point, pseudo-random number in the range [0, 1) that is, from 0 (inclusive) up to but not including 1 (exclusive), which you can then scale to your desired range. The implementation selects the initial seed to the random number generation algorithm; it cannot be chosen or reset by the user.

But there is a hack that works on Apps Script and all the browsers I've tried, that allows you to generate repeatable random sequences by poking Math.seed. 

Both Lucky.shuffle and Lucky.get accept a seed, so that means that there can also be a Lucky.unShuffle, which I'll cover shortly. A seed is a non zero, numeric value that can be used to force the random number generator to return the same sequence when used. Lucky accepts both numeric and string values as a seed, so you can use a passphrase instead of a number if you prefer.

Get 2 arrays of length 10 populated with random numbers between  1 and 100. Since I've used the same seed with this, then the result is the same in both cases. A different seed will generate a different result.
var options = {
  min: 1,
  max: 100,
  seed: 90,
  size:10
};
for (var i = 0; i < 2 ; i++) {
  show('Array seeded '+i, Lucky.get(options));
}
Array seeded 0
[80,95,15,10,99,76,100,44,26,92]
Array seeded 1
[80,95,15,10,99,76,100,44,26,92]

Get 2 arrays of length between 6 and 15 populated with random numbers between  1 and 100. Since I've used the same seed with this, then the result is the same in both cases. A different seed will generate a different result. Note that the start of the arrays, which are different sizes, is the same.
var options = {
  min: 1,
  max: 100,
  maxSize: 15,
  seed: 90,
  size: 6
};
for (var i = 0; i < 2 ; i++) {
  show('Array seeded random size '+i, Lucky.get(options));
}
Array seeded random size 0
[80,95,15,10,99,76,100,44,26,92,73,7,29]
Array seeded random size 1
[80,95,15,10,99,76,100,44,26]

Lucky.unShuffle(array, seed)

Now that we have a way of shuffling an array in a predictable away, restoring to the original order is a simple matter of reversing the shuffle by using the same seed as was used in the original shuffle, as per these examples.

We'll use this array
var arr = ["barney", "fred", "wilma", "betty", "bambam", "pebbles"];
show('Shuffling arrays with seeds', arr);
Shuffling arrays with seeds
["barney","fred","wilma","betty","bambam","pebbles"]

And shuffle it using some seed
var seed = 123.456;
var shuffled = Lucky.shuffle(arr, seed);
show("Shuffled array with seed", shuffled);
Shuffled array with seed
["pebbles","betty","wilma","barney","fred","bambam"]

And magically, we can use the same seed to remember the original sequence
show("Unshuffled array with seed", Lucky.unShuffle(shuffled, seed));
Unshuffled array with seed
["barney","fred","wilma","betty","bambam","pebbles"]

Lucky.getString (options)

All the same techniques are available to manipulate strings, plus a few others that are specific to strings.

Generate 2 random strings of 10 characters using the characters a-z
var options = {
  min: 'a',
  max: 'z',
  size: 10
};

for (var i = 0; i < 2 ; i++) {
  show('String '+i, Lucky.getString(options));
}

String 0
ifeihbebje
String 1
fafceiejgh

Generate 2 random strings of between 7 and 18 characters using the characters A-S
var options = {
  min: 'A',
  max: 'S',
  maxSize: 18,
  size:7
};

for (var i = 0; i < 2 ; i++) {
  show('String random size '+i, Lucky.getString(options));
}
String random size 0
DFFBPDCKEKIMNOEPEL
String random size 1
ACADBFF

Strings can also be shuffled
show('string shuffle', Lucky.shuffle('abcdefghijklmnopqrstuvwxyz'));
string shuffle
ivceqjduphlmkosnytbxfgrzaw

You can also use seeds with strings. Generate 2 strings of length 10, using some seed. Note that both strings are in the same order.
var options = {
  min: 'a',
  max: 'q',
  size: 10,
  seed: 90
};
for (var i = 0; i < 2 ; i++) {
  show('String seeded '+i, Lucky.getString(options));
}
String seeded 0
hjbajhjecj
String seeded 1
hjbajhjecj

Generate a string of between 6 and 15 characters, using characters d-v. Since we're using a seed, both strings will start with the same characters.
var options = {
  min: 'd',
  max: 'v',
  maxSize: 15,
  seed: 90,
  size: 6
};
for (var i = 0; i < 2 ; i++) {
  show('String seeded random size '+i, Lucky.getString(options));
}
String seeded random size 0
sufevrvlhuqe
String seeded random size 1
sufevrvlhuqeih

Seeds can be used with strings, to create strings that can be unshuffled again. Let's use this string
var str="democracy is a device that ensures we shall be governed no better than we deserve";
show('Shuffling strings with seeds', str);
Shuffling strings with seeds
democracy is a device that ensures we shall be governed no better than we deserve

Let's shuffle using a seed.
var seed = 1.67;
var shuffled = Lucky.shuffle(str, seed);
show("Shuffled string with seed", shuffled);
Shuffled string with seed
tn eerbaehe erco sece nytlnescbd r ehatv ld ounmvssiwtve e eheoragraedistead w

Using the same seed, we can get back to the original order.
show("Unshuffled string with seed", Lucky.unShuffle(shuffled, seed));
Unshuffled string with seed
democracy is a device that ensures we shall be governed no better than we deserve


Strings as seeds

So far we've just used numbers as a seed. Lucky also supports a seed as a string , which may be more convenient, as per this example, which shuffles an unshuffles which a string seed.

var str="Using a string as a seed";

show('strings as seeds', str);
var seed = 'your passcode';
var shuffled = Lucky.shuffle(str, seed);
show("Shuffled string with string seed", shuffled);
show("Unshuffled string with string seed", Lucky.unShuffle(shuffled, seed));

strings as seeds
Using a string as a seed
Shuffled string with string seed
sn seeta ng sdgriaUsai
Unshuffled string with string seed
Using a string as a seed

Regular expressions to specify the qualifying character set

Using the options.min and options.max, you can specify the range of characters that can be used in a random string, but sometimes you need a more specific character set. You can use regular expressions to limit the character set to characters that match your regular expression.
Get two random strings of 30 characters consisting only of characters that can appear in words (that match the regex expression \w).
var options = {
  size: 30,
  rx:/\w/
};

for (var i = 0; i < 2 ; i++) {
  show('String with regex '+i, Lucky.getString(options));
}
String with regex 0
dhWgRnW4GwfRITz8JvRTdQJm_MPVJl
String with regex 1
3GRm28odxq_Uf5lDYPdO79jn9yVHgC

The other features such as seeding and variable size can also be used with regular expression character set selection.
Get two strings between 1 and 13 characters made with characters that match the regular expression [A-Gb#].
var options = {
  maxSize: 13,
  size: 1,
  rx:/[A-Gb#]/
};

for (var i = 0; i < 2 ; i++) {
  show('String random size with regex '+i, Lucky.getString(options));
}
String random size with regex 0
EbDE#FbGDFCGG
String random size with regex 1
#CDACACG

Character sets and regular expressions

By default, when using regular expressions, only standard ascii characters are included in the generated string. However you can also include extended ascii (for those accented characters), but UTF-8 encodings are not yet supported.  You can get extended ascii by specifying {extendedAscii: true } in your options.

Using lists

Lists can be used to specify the character set. This means you can limit the characters included to those in a list. Whereas regular expressions can only be used with getString, Lists can be used with both get and getString.

Get two strings of between 12 and 13 characters made up only from the characters 'abcdef'
var options = {
  maxSize: 13,
  size: 12,
  list:'abcdef'
};

for (var i = 0; i < 2 ; i++) {
  show('String random size with list '+i, Lucky.getString(options));
}
String random size with list 0
fecfcefabbaba
String random size with list 1
abdadccdebffa

One advantage of using lists is that you can introduce bias by having the same character repeated multiple times. This example uses the letter frequency in scrabble along with the average length of english words to make sentences of nonsense, that actually look a little like  sentences.
var scrabble = "aaaaaaaaabbccddddeeeeeeeeeeeeffggghhiiiiiiiiijkllllmmnnnnnnooooooooppqrrrrrrssssttttttuuuuvvwwxyyz"
var spaces = scrabble.split("").slice(0,Math.round(scrabble.length/5.1) + 1).join (" ");

var options = {
  maxSize: 300,
  size: 200,
  list:spaces+scrabble
};
for (var i = 0; i < 2 ; i++) {
  show('string random size with scrabble list '+i, Lucky.getString(options));
}

string random size with scrabble list 0
ehcr ma kftguiau r y acrtono go jazbanqsigaytwidrudoliai neccafcdabeo qai catetdeegbtr aaaypuah rgm ghegrqoaa dptiieeovaav eisolp a tkgtgesiubmceihcmrfvtranxhubowe i daatsvh qaoccmh eag naaegbieaa oaserwglbhen a ws vkmoafaeaaee rd i tao ateatib puaftbcaua dj bsar ae y q i rppv e ognxz d twwe
string random size with scrabble list 1
htupamidlcqegspaert nnooetftado set oeq orklu aofidjdvuauubng tas asbeern rrlbwnaddblrrobxeqoceefla iasieenaocteqt owi assu keatdaadiioogiykn oezeaifga ga r dacsoag oisaysosv srbr le cmadebeliiipce andsjibjnraceeaav ojg ptrv ovfcwfaaworp qaeysuopga aiplaeuailauonfotearzk ypztgpeewodreieri aa


To use lists with arrays, just populate the array with the members you want.
Get 2 arrays of between 5 and 10 members, made up from the members of the list, ['george','john','paul','ringo']
var options = {
  maxSize: 10,
  size: 5,
  list:['george','john','paul','ringo']
};
for (var i = 0; i < 2 ; i++) {
  show('an array with a list '+i, Lucky.get(options));
}
an array with a list 0
["paul","john","ringo","john","ringo","george","john","george"]
an array with a list 1
["ringo","paul","paul","paul","john","john","paul"]

Seeds can be used with lists too. 
Get 2 arrays of  6 members, made up from the members of the list, ['george','john','paul','ringo'], but use a seed so it can be repeatable
var options = {
  size: 6,
  list:['george','john','paul','ringo'],
  seed:'my passphrase'
};
for (var i = 0; i < 2 ; i++) {
  show('an array with a list seeded '+i, Lucky.get(options));
}
an array with a list seeded 0
["george","paul","john","ringo","george","ringo"]
an array with a list seeded 1
["george","paul","john","ringo","george","ringo"]


Lucky.getGrid (options)

There's also a built in way of creating a grid of random values. Useful for spreadsheets test data.
var options = {
  size:6,
  width:3
};
show('getting grid', Lucky.getGrid(options));
getting grid
[["1-date","2-string","3-boolean"],["2016-11-19T16:48:49.222Z","iavdyvwdff",true],["2016-11-15T22:16:56.273Z","nifgnklkjb",false],["2016-11-22T19:54:26.532Z","ktajypvbso",true],["2016-11-08T04:41:04.996Z","uwsjdttvua",true],["2016-11-13T20:00:57.898Z","qeknkrpoic",false],["2016-11-21T19:09:40.212Z","ciiopbfhqk",false]]

All the options for seeding and lists etc are available here too. By default it will create a random number of each of boolean,date,string and number columns using the width and maxWidth properties to decide how many to make. It uses a list option to know which kind of data to make, the default is ["date","boolean","string","number"]. If you want a different set, you can  do something like this:
var options = {
  size:6,
  width:3, 
  list:["number", "string"]
};

If you want to keep the the columns to a specific list (not randomized), you can used the fixed option, for example
var options = {
  size:8,
  list:["number", "string","string"],
  fixed:true
};
getting grid
[["1-number","2-string","3-string"],[19,"phrrfoeqwk","spavlweeqb"],[60,"stuvsbqvir","pelnhwvpdo"],[73,"aakbcjwsit","iwtrswadax"],[12,"nynlvgqmxc","fmdcgbexol"],[37,"tfahjosrqd","wjmnakhtpq"],[19,"giteynnkxu","jzcqwtlfdt"],[96,"tssxtykynl","oplfvzsolc"],[35,"uzmktnguup","ylrcydnylq"]]

There are some defaults for each of the data types, but you can change that too, using all the same parameters as covered previously, as in this example which will create 12 rows of 3 columns with random data driven by the options shown.
var options = {
  size: 12,
  fixed: true,
  list: ["number", "string", "date"],
  number: {
    max: 100,
    min: 20,
  },
  date: {
    max: new Date(2017, 3, 1),
    min: new Date(2016, 1, 2)
  },
  string: {
    size: 10,
    maxSize: 20,
    rx: /[a-k0-9]/
  }
};
getting grid
[["1-number","2-string","3-date"],[20,"50jg7i3ba70kj7cf5","2016-11-21T05:08:31.047Z"],[58,"8jabggch93d16k7gifj","2016-04-19T18:40:11.470Z"],[66,"3d62j59adabcb7b191","2017-02-10T23:16:30.828Z"],[83,"f5f2d2c7abh9cd8a7ejh","2016-07-25T21:31:28.729Z"],[22,"4ka2ik9866ejfbf73270","2016-05-31T01:09:47.475Z"],[91,"4g4j66jd6ib676eb6e1","2016-02-13T03:05:23.132Z"],[25,"bd422dcbc46ddia7g","2016-03-15T01:45:25.842Z"],[95,"7k72ak0i9dfkfd0j8265","2016-03-29T10:16:33.933Z"],[47,"d7d4006c9kheg2bjg","2017-02-10T10:01:18.654Z"],[81,"b481d83307ba79k6d12","2017-03-12T11:28:41.816Z"],[48,"5i656ib22e03ij4k67g","2017-03-28T17:07:22.291Z"],[54,"hhe5c2d5gdfci09e3jj5","2016-09-28T19:19:17.504Z"]]

Seed inheritance

In the getGrid() example, each type of column can have its own seed - meaning that you can reproduce the results  for a particular column if you run it again. By default, if the main options have a seed mentioned, then this seed will be inherited by each column (unless they have their own) - like this the entire grid can be reproduced by setting a single seed, so for example, this will always produce the same grid as in the example below.
var options = {
  size: 4,
  seed: 'a seed',
  fixed: true,
  list: ["number", "string", "date"],
  number: {
    max: 100,
    min: 20,
  },
  date: {
    max: new Date(2017, 3, 1),
    min: new Date(2016, 1, 2)
  },
  string: {
    size: 4,
    maxSize: 8,
    rx: /[a-k]/
  }
};
getting grid seed 1
[["1-number","2-string","3-date"],[22,"aecdcchf","2016-02-14T09:44:32.068Z"],[50,"aecdcchf","2016-07-11T10:46:47.497Z"],[37,"aecdcchf","2016-05-05T21:40:24.208Z"],[44,"aecdcchf","2016-06-08T18:24:18.811Z"]]
getting grid seed 2
[["1-number","2-string","3-date"],[22,"aecdcc","2016-02-14T09:44:32.068Z"],[50,"aecdcc","2016-07-11T10:46:47.497Z"],[37,"aecdcch","2016-05-05T21:40:24.208Z"],[44,"aecdcchf","2016-06-08T18:24:18.811Z"]]

You can change the behavior for any of the individual columns, by turning that off. The example below would be able to repeat the grid for all columns except for string columns, which have inheritSeed turned off.
var options = {
  size: 4,
  seed: 'a seed',
  fixed: true,
  list: ["number", "string", "date"],
  number: {
    max: 100,
    min: 20,
  },
  date: {
    max: new Date(2017, 3, 1),
    min: new Date(2016, 1, 2)
  },
  string: {
    size: 4,
    maxSize: 8,
    rx: /[a-k]/,
   inheritSeed:false
  }
};
getting grid seed 3 - no string inheritance
[["1-number","2-string","3-date"],[22,"aifijc","2016-02-14T09:44:32.068Z"],[50,"bkghbida","2016-07-11T10:46:47.497Z"],[37,"edaijaeb","2016-05-05T21:40:24.208Z"],[44,"jebgccdf","2016-06-08T18:24:18.811Z"]]

Other stuff

There are a few other utilities in this namespace that can be handy

Get a random  integer between 200 and 399
show ('getRandBetween',Lucky.getRandBetween (200,399));
getRandBetween
280
Get a unique string of the recommended size
show ('getUniqueString',Lucky.getUniqueString ());
getUniqueString
ekm0n3b15adf

Get a unique string of the minimum size that can work
show ('getUniqueString shorter',Lucky.getUniqueString (9));
getUniqueString shorter
d3f0oa1b5

Get a longer unique string of 20 characters
show ('getUniqueString longer',Lucky.getUniqueString (20));
getUniqueString longer
kkrgtesrcdhfad015bo3

Methods

 Lucky methods description
 .get(options)  gets an array of random stuff as defined by the options
 .getString(options) gets a random string as defined by the options
 .getGrid(options) gets a random grid as defined by the options
 .shuffle(array|string[,seed]) shuffles an array or a string (repeatably if seed is specified)
 .unShuffle(array|string,seed) can restore an array or string to its original order using the same seed it was shuffled with
 .getRandBetween (min,max[,func]) gets a random  integer between min and max using Math.random(), or some other function if supplied
 .getUniqueString([size]) gets a unique string of the given size

Options

Not all options apply to all methods. See the description and examples earlier for details
 Lucky options type description
 max number the max value to allow in the generated array or integer array
 min number the min value to allow in the generated array or integer array
 size number the size of the string or array to generate, or the minimum size if maxSize is specified. For grids this refers to the number of rows in the grid.
 maxSize number where specified, will generate a string or array of a random length between size and maxSize
 seed number|string if specified, creates a repeatable random string or array
 width number used with getGrid to specify the minimum width of the grid, or the actual width if maxWidth is not specified
 maxWidth number where specified, will generate a grid of a random width between width and maxWidth
 list array|string a list of acceptable values or characters to randomize. This overrides max/min values. In the case of a grid, list is used to communicate the types of columns required. The default is ["boolean","string","number","date"]
 rx RegExp Any characters matching this expression will make the list of characters to randomize. This overrides both list and min/max
 extendedAscii boolean if true, then 8 bits ascii is used to match against an rx. The default is 7 bit standard ascii. Only relevant with an rx
 number object the properties above can be set in this object to set the behavior for generated columns of the type number with getGrid. Only relevant if 'number' is included in the list option of a getGrid request.
 boolean object the properties above can be set in this object to set the behavior for generated columns of the type boolean with getGrid. Only relevant if 'boolean' is included in the list option of a getGrid request.
 string object the properties above can be set in this object to set the behavior for generated columns of the type string with getGrid. Only relevant if 'string' is included in the list option of a getGrid request. min and max are ignored for boolean, but you can use a list of different numbers of trues and falses to introduce bias if required.
 date object the properties above can be set in this object to set the behavior for generated columns of the type date with getGrid. Only relevant if 'date' is included in the list option of a getGrid request.For a date, min and max should be set as a date object.

Showing results

All these examples used a  function to log results. Of course these would be different depending on whether its apps script or browser based. Here's both versions.
function show(div, stuff) {
  //------ browser version
  /**
  var id = div.replace(/\s/g, "-");
  var el = document.getElementById(id) || createElement(document.body, "div", id);
  el.innerHTML = "<strong>" + div + "</strong><br>"
  el.innerHTML += typeof stuff === "object" ? JSON.stringify(stuff) : stuff;
  
  function createElement(parent, type, id) {
    var elem = document.createElement(type);
    if (typeof id !== typeof undefined) {
     elem.id = id;
    }
    parent.appendChild(elem);
    return elem;
  }
  */

  //-----apps script version
  Logger.log("\n" + div + "\n" + (typeof stuff === "object" ? JSON.stringify(stuff) : stuff));
  //------
  return stuff
}







For more like this, see Google Apps Scripts snippets. Why not join our forum, follow the blog or follow me on twitter to ensure you get updates when they are available.
Comments