diff --git a/src/random/number.ts b/src/random/number.ts index 535abd8..2e68387 100644 --- a/src/random/number.ts +++ b/src/random/number.ts @@ -1,18 +1,5 @@ export const defaultRng = Math.random; -function sample(from: ReadonlyArray, count: number, rng = defaultRng): T[] { - const sampled: T[] = from.slice(0, count); // 'reservoir' - - for (let i = count; i < from.length; i++) { - const j = Math.floor(1 + Math.random() * i); - if (j < count) { - sampled[j] = from[i]; - } - } - - return sampled; -} - export function binomialRandom(n: number, p: number, rng = defaultRng): number { let s = 0; for (let sample = 0; sample < n; ++sample) { diff --git a/src/stress/__snapshots__/sample.spec.ts.snap b/src/stress/__snapshots__/sample.spec.ts.snap new file mode 100644 index 0000000..13ac4a6 --- /dev/null +++ b/src/stress/__snapshots__/sample.spec.ts.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sample picks random value: sample([1,2,3]) 1`] = `1`; + +exports[`sampleSize picks specified size: sampleSize([1,2,3], 2) 1`] = ` +[ + 1, + 2, +] +`; diff --git a/src/stress/sample.spec.ts b/src/stress/sample.spec.ts new file mode 100644 index 0000000..2cf65fa --- /dev/null +++ b/src/stress/sample.spec.ts @@ -0,0 +1,23 @@ +import { sample, sampleSize } from './sample'; + +function fakeRng() { + return 0.3; +} + +describe('sample', () => { + it('picks random value', () => { + expect(sample([1, 2, 3], fakeRng)).toMatchSnapshot('sample([1,2,3])'); + }); + it('returns undefined on empty array', () => { + expect(sample([])).toBeUndefined(); + }); +}); + +describe('sampleSize', () => { + it('picks specified size', () => { + expect(sampleSize([1, 2, 3], 2, fakeRng)).toMatchSnapshot('sampleSize([1,2,3], 2)'); + }); + it('returns undefined on empty array', () => { + expect(sampleSize([2, 3], 3, fakeRng)).toHaveLength(2); + }); +}); diff --git a/src/stress/sample.ts b/src/stress/sample.ts new file mode 100644 index 0000000..3132f0e --- /dev/null +++ b/src/stress/sample.ts @@ -0,0 +1,19 @@ +import { defaultRng } from '../random/number'; + +export function sample(input: readonly T[], rng = defaultRng): T { + const index = Math.floor(input.length * rng()); + return input[index]; +} + +export function sampleSize(from: T[], count: number, rng = defaultRng): T[] { + const reservoir: T[] = from.slice(0, Math.min(count, from.length)); + + for (let i = count; i < from.length; i++) { + const j = Math.floor(1 + Math.random() * i); + if (j < count) { + reservoir[j] = from[i]; + } + } + + return reservoir; +}