# core/selectors/counter.selector.ts

> 44 lines of code and 12 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-selectors-counter-selector-ts
Source text: https://banes-lab.com/source/patterns/core/selectors/counter.selector.ts.txt

Listed in [core/selectors](https://banes-lab.com/api/source/patterns/core/selectors.md), after [core/selectors/code.selector.ts](https://banes-lab.com/source/patterns/core/selectors/code.selector.ts.md) and before [core/selectors/record.selector.ts](https://banes-lab.com/source/patterns/core/selectors/record.selector.ts.md).

## Definitions

- `topEntries` (lexical_declaration, line 8, exported)
- `weightedChoice` (lexical_declaration, line 29, exported)
- `pickIndex` (lexical_declaration, line 12, exported)
- `pick` (lexical_declaration, line 16)
- `weightedSample` (lexical_declaration, line 34, exported)
- `byCountDesc` (lexical_declaration, line 4, exported)
- `remaining` (lexical_declaration, line 17)
- `last` (lexical_declaration, line 18)
- `total` (lexical_declaration, line 30, exported)
- `pool` (lexical_declaration, line 39, exported)
- `drawn` (lexical_declaration, line 40, exported)
- `picked` (lexical_declaration, line 42, exported)

## Contained in

- [core/selectors](https://banes-lab.com/anatomy/patterns/folder-patterns-core-selectors.md)

## Used by

- [core/aggregators/representation.distribution.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.distribution.aggregator.ts.md)
- [core/aggregators/representation.graph.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.graph.aggregator.ts.md)
- [core/aggregators/representation.sequence.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.sequence.aggregator.ts.md)
- [core/aggregators/representation.tree.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.tree.aggregator.ts.md)
- [core/loaders/package.loader.ts](https://banes-lab.com/source/patterns/core/loaders/package.loader.ts.md)

## Linked from

- [core/aggregators](https://banes-lab.com/anatomy/patterns/folder-patterns-core-aggregators.md)
- [core/loaders](https://banes-lab.com/anatomy/patterns/folder-patterns-core-loaders.md)
- [core/selectors](https://banes-lab.com/anatomy/patterns/folder-patterns-core-selectors.md)

## Source

```typescript
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;
};
```
