Search code examples
javascripttypescriptrandomlodash

How to seed lodash random number generator?


How can I seed the lodash rng so that the random choices for the shuffle function are reproducible?

E.g.

import _ from 'lodash'

console.log(_.shuffle([1,2,3,4]))

Run #1 output: [1,3,4,2]

Run #2 output: [3,2,1,4]

Run #3 output: [4,1,2,3]

The result is always different.


Solution

  • lodash uses its own Math.random under the hood, but it is independent from the global Math.random, so using a library like seedrandom to make Math.random seedable does not work.

    Instead, we have to make use of lodash's runInContext() function which uses the current global state instead of using it's own hidden-away version. Combining this with a seedable rng like seedrandom we can use the following to get a seeded version of lodash:

    import _lodash from 'lodash'
    import seedrandom from 'seedrandom'
    
    // create a new lodash instance with the given seed
    export const seedLodash = (seed: number | string) => {
        // take a snapshot of the current Math.random() fn
        const orig = Math.random
        // replace Math.random with the seeded random
        seedrandom(seed, { global: true })
        // runInContext() creates a new lodash instance using the seeded Math.random()
        // the context is a snapshot of the state of the global javascript environment, i.e. Math.random() updated to the seedrandom instance
        const lodash = _lodash.runInContext()
        // restore the original Math.random() fn
        Math.random = orig
        // return the lodash instance with the seeded Math.random()
        return lodash
    }
    

    This creates an rng which replaces Math.random, makes a new lodash instance referencing the modified Math.random, then replaces Math.random with the original unmodified (i.e. unseeded) version.

    If you want to seed both lodash and Math.random simply add these two lines at the top of your code:

    import _lodash from 'lodash'
    import seedrandom from 'seedrandom'
    
    seedrandom(seed, { global: true });
    const _ = _lodash.runInContext();
    // use lodash and Math.random as normal...