# core/selectors/metric.selector.ts

> 15 lines of code and 5 definitions.

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

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

## Definitions

- `percentile` (lexical_declaration, line 1, exported)
- `index` (lexical_declaration, line 5, exported)
- `tally` (lexical_declaration, line 9, exported)
- `counts` (lexical_declaration, line 10, exported)
- `key` (lexical_declaration, line 12, exported)

## Contained in

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

## Used by

- [core/converters/metric.converter.ts](https://banes-lab.com/source/stats/core/converters/metric.converter.ts.md)

## Linked from

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

## Source

```typescript
export const percentile = function percentile(sortedAscending: readonly number[], fraction: number): number {
    if (sortedAscending.length === 0) {
        return 0;
    }
    const index = Math.min(sortedAscending.length - 1, Math.floor(fraction * sortedAscending.length));
    return sortedAscending.at(index) ?? 0;
};

export const tally = function tally<T>(items: readonly T[], pick: (item: T) => string): Map<string, number> {
    const counts = new Map<string, number>();
    for (const item of items) {
        const key = pick(item);
        counts.set(key, (counts.get(key) ?? 0) + 1);
    }
    return counts;
};
```
