# core/converters/metric.converter.ts

> 27 lines of code and 6 definitions.

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

Listed in [core/converters](https://banes-lab.com/api/source/stats/core/converters.md), before [core/converters/taxonomy.converter.ts](https://banes-lab.com/source/stats/core/converters/taxonomy.converter.ts.md).

## Definitions

- `deriveMetrics` (lexical_declaration, line 12, exported)
- `EMPTY_BUCKET` (lexical_declaration, line 6)
- `bucketOf` (lexical_declaration, line 8, exported)
- `sortedLines` (lexical_declaration, line 13, exported)
- `source` (lexical_declaration, line 14, exported)
- `authoredFiles` (lexical_declaration, line 15, exported)

## Contained in

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

## Uses

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

## Linked from

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

## Source

```typescript
import type { Bucket, State } from "#types/source.types";
import { MEDIAN, NINETIETH, TOP_LARGEST } from "#configuration/constants/metric.constants";
import type { Derived } from "#types/report.types";
import { percentile } from "#core/selectors/metric.selector";

const EMPTY_BUCKET: Bucket = { blank: 0, bytes: 0, code: 0, files: 0, total: 0 };

export const bucketOf = function bucketOf<K>(buckets: ReadonlyMap<K, Bucket>, key: K): Bucket {
    return buckets.get(key) ?? EMPTY_BUCKET;
};

export const deriveMetrics = function deriveMetrics(state: State): Derived {
    const sortedLines = state.fileLineCounts.toSorted((a, b) => a - b);
    const source = bucketOf(state.authored, "source");
    const authoredFiles = [...state.authored.values()].reduce((sum, bucket) => sum + bucket.files, 0);
    return {
        areaRows: [...state.byArea.entries()].toSorted((a, b) => b[1].code - a[1].code),
        authoredFiles,
        authoredLines: state.lines.code,
        avgLines: authoredFiles > 0 ? Math.round(state.lines.total / authoredFiles) : 0,
        emptyFiles: state.fileLineCounts.filter((count) => count === 0).length,
        extRows: [...state.byExt.entries()].toSorted((a, b) => b[1].code - a[1].code),
        largestLines: sortedLines.at(-1) ?? 0,
        medianLines: percentile(sortedLines, MEDIAN),
        p90Lines: percentile(sortedLines, NINETIETH),
        sourceBlank: source.blank,
        sourceLines: source.code,
        topLargest: state.largest.toSorted((a, b) => b.lines - a.lines).slice(0, TOP_LARGEST),
    };
};
```
