npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

rerandom

v1.0.2

Published

deterministic randomization

Downloads

9

Readme

rerandom.js

repeatable randomization for JavaScript

Overview

This is a randomization function which keeps track of the situations in which it is used. It will return a random value the first time it sees an input, and will then remember it and return the same output value for any subsequent uses of that input. Random first, then consistent.

How

ECMAScript 6 finally provides proper Maps so we no longer have to use plain objects to track key/value pairs. With Maps, keys don't necessarily have to be strings -- they can now be functions, objects, arrays, or whatever else you want. rerandom.js uses a Map to keep track of your inputs, which roughly represent the contexts in which you are using randomization, and it associates them with the results of the initial randomization. Essentially, it bundles random numbers, key/value tracking, and a few useful helper functions into a single tool so you can more easily use controlled, constrained randomization.

(Naturally, this requires ES6 Maps. If you don't have them in your environment, try one of the many available polyfills.)

Installation

Install via npm:

npm install rerandom

Or just include as a script tag:

<script type="text/javascript" src="path/to/rerandom.js"></script>

API

rerandom(factory);

Factory which returns an instance of the rerandom function. Each instance tracks previous values independently.

var rr = rerandom();

instance(input);

The main instance function returns a random float value between 0 and 1, just like Math.random(). It takes an optional input, and will return consistent output values whenever an input value is repeated.

The decision to reuse an existing previously randomized output value is based on JavaScript memory addressing. Anything that would pass an equality test will produce a consistent output value.

rr(4) === rr(4); // true
rr([]) === rr([]); // false
var a = [];
var b = [];
var c = a;
rr(a) === rr(b); // false
rr(a) === rr(c); // true

instance.registry(reset);

Returns or resets the internal Map object used as a registry for tracking inputs. If the optional reset argument is null, then the registry will be overwritten, and you'll start over with a clean slate again.

// reset the internal registry
a = rr(5);
rr.registry(null);
b = rr(5);
a === b; // false

// get the internal registry
let previous = rr.registry()
previous.has(5) // true

instance.generator(function);

Gets or sets a custom random number generator. Math.random() is used by default, but you can also roll your own using the Web Crypto API or other tools.

// always return the same integer (note: this is
// not a very good random number generator)
rr.generator(function() {
  return 42;
});
rr(5) === 42; // true

// replace the float generated by Math.random
// with an integer generated by the Web Crypto API
rr.generator(function() {
  return window.crypto.getRandomValues(new Uint32Array(1))[0];
});

Custom random number generators passed into the instance must be functions which always return numbers, and you'll get an error if you try to use anything else. To convert the random number outputs into arbitrary alternate types which may be more contextually useful, use the .post() method.

instance.post(function);

Gets or sets a post-processing function to be run on the results of the randomization. The float values returned by standard randomization often aren't immediately useful, so this is a way to bundle your desired behavior with the instance.

// return a float between 3 and 4
rr.post(function(value) {
  return value + 3;
});
rr(5) > 3 && rr(6) < 4; // true

// cast to string
rr.post(function(value) {
  return value + '';
});
typeof rr(7) === 'string'; // true

Post-processing functions can take up to two arguments: the output value of the random number generator and the original input. This allows you to create randomized outputs which are aware of the inputs. For example, to select a random item from an input array, you could do the following:

rr.post(function(value, array) {
    let index = Math.floor(value * array.length);
    return array[index];
});

You can also omit the input key and avoid updating the internal registry, which is clean a way to use the post-processing wrapper around Math.random.

rr.post(function(value) {
  let max = 10;
  return Math.floor(value * max) + 1;
});
rr(); // now always returns a value between 1 and 10

instance.key(function)

Gets or sets a preprocessing function which is applied to inputs before they are used as Map keys. Equality testing in JavaScript relies on memory addressing, such that primitives match more readily than objects and other complex data structures, but that is not always the desired behavior. This method attaches a function which can be used to increase the number of matches by extracting a primitive from a data structure, coercing a data structure into a primitive, flattening an array into a string, and so on.

For example, to get array literal inputs to more readily match up with previous values:

// array literals do not match by default
rr(['a', 'b', 'c']) === rr(['a', 'b', 'c']); // false

// flatten input array to a string before using as the Map key
rr.key(function(array) {
  return array + '';
});
rr(['a', 'b', 'c']);
rr.registry().has('a,b,c'); // true

// array literals now match based on key preprocessing
rr(['a', 'b', 'c']) === rr(['a', 'b', 'c']); // true

Or alternatively, to use the sum of all integers in the input array as the Map key:

rr.key(function(array) {
  return array.reduce(function(a, b) {return a + b;});
});
rr([1, 2]);
rr.registry().has(3); // true

Key processing functions take one argument, which is the input value, and must return whatever is to be used as the Map key, which in many cases is likely to be a string.

Further

  • Consider experimenting with networking multiple instances, such that the output of one instance is both used directly and also then fed into another instance. This may produce interesting effects, especially if the .key() and .post() methods are adjusted to this use case.
  • Randomization is deterministic within the context of a running page or application, but the record is reset whenever the page is reloaded, so values will not be deterministic across different page loads. For the latter, you'll need a random number generator that can be seeded; David Bau's seedrandom.js is a good option.