core/selectors/counter.selector.ts
core/selectors/counter.selector.ts is a file in GovLab Patterns. 44 lines of code and 12 definitions.
import type { Rng } from "#types/seed.types";
import { sumOf } from "#core/counters/base.counter";
export const byCountDesc = function byCountDesc(a: readonly [string, number], b: readonly [string, number]): number {
return b[1] - a[1] || a[0].localeCompare(b[0]);
};
export const topEntries = function topEntries(counter: ReadonlyMap<string, number>, limit: number): [string, number][] {
return [...counter.entries()].sort(byCountDesc).slice(0, limit);
};
export const pickIndex = function pickIndex(rng: Rng, length: number): number {
return Math.floor(rng.next() * length);
};
const pick = function pick(weights: ReadonlyMap<string, number>, threshold: number): string | null {
let remaining = threshold;
let last: string | null = null;
for (const [key, weight] of weights) {
last = key;
remaining -= weight;
if (remaining < 0) {
return key;
}
}
return last;
};
export const weightedChoice = function weightedChoice(rng: Rng, weights: ReadonlyMap<string, number>): string | null {
const total = sumOf(weights.values());
return total <= 0 ? null : pick(weights, rng.next() * total);
};
export const weightedSample = function weightedSample(
rng: Rng,
weights: ReadonlyMap<string, number>,
size: number,
): string[] {
const pool = new Map(weights);
const drawn: string[] = [];
while (drawn.length < size && pool.size > 0) {
const picked = weightedChoice(rng, pool);
if (picked === null) {
return drawn;
}
drawn.push(picked);
pool.delete(picked);
}
return drawn;
};