Reservoir Sampling For Streams
Uniformly sample a fixed number of items from arrays or one-pass iterables using reservoir sampling with an injectable random source.
Code
export function reservoirSample(items, size, random = Math.random) {
if (!Number.isInteger(size) || size < 0) {
throw new RangeError("size must be a non-negative integer");
}
if (size === 0) return [];
const reservoir = items.slice(0, size);
for (let index = size; index < items.length; index += 1) {
const candidate = Math.floor(random() * (index + 1));
if (candidate < size) reservoir[candidate] = items[index];
}
return reservoir;
}
export function* reservoirStream(iterable, size, random = Math.random) {
if (!Number.isInteger(size) || size < 0) {
throw new RangeError("size must be a non-negative integer");
}
if (size === 0) return;
const reservoir = [];
let seen = 0;
for (const item of iterable) {
if (seen < size) {
reservoir.push(item);
} else {
const candidate = Math.floor(random() * (seen + 1));
if (candidate < size) reservoir[candidate] = item;
}
seen += 1;
}
yield* reservoir;
}Report a problem with this exampleOpens a pre-filled issue on GitHub.