# core/normalizers/word.normalizer.ts

> 62 lines of code and 16 definitions.

Tree: Build tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/build#file-build-core-normalizers-word-normalizer-ts
Source text: https://banes-lab.com/assets/sources/source.ea73d92e84f694f1d8167c80ffa8e3ea5954b0d9227dfea9621198085533dcae.generated.txt

## Definitions

- `isBoundary` (lexical_declaration, line 26)
- `countTerm` (lexical_declaration, line 30, exported)
- `digitMetricsOf` (lexical_declaration, line 69, exported)
- `lowerWordsOf` (lexical_declaration, line 22, exported)
- `countAny` (lexical_declaration, line 44, exported)
- `isNumeric` (lexical_declaration, line 52)
- `isDigitMetric` (lexical_declaration, line 64, exported)
- `wordsOf` (lexical_declaration, line 3, exported)
- `isDigit` (lexical_declaration, line 48)
- `words` (lexical_declaration, line 4, exported)
- `current` (lexical_declaration, line 5, exported)
- `count` (lexical_declaration, line 31, exported)
- `from` (lexical_declaration, line 32, exported)
- `before` (lexical_declaration, line 34, exported)
- `after` (lexical_declaration, line 35, exported)
- `unit` (lexical_declaration, line 65, exported)

## Used by

- [core/analyzers/tone.analyzer.ts](https://banes-lab.com/source/build/core/analyzers/tone.analyzer.ts.md)

## Source

```typescript
import { WORD_STOPS } from "#configuration/constants/tone.constants";

export const wordsOf = function wordsOf(text: string): string[] {
    const words: string[] = [];
    let current = "";
    for (const char of text) {
        if (WORD_STOPS.has(char)) {
            if (current.length > 0) {
                words.push(current);
            }
            current = "";
        } else {
            current += char;
        }
    }
    if (current.length > 0) {
        words.push(current);
    }
    return words;
};

export const lowerWordsOf = function lowerWordsOf(text: string): string[] {
    return wordsOf(text).map((word) => word.toLowerCase());
};

const isBoundary = function isBoundary(char: string): boolean {
    return char.length === 0 || WORD_STOPS.has(char);
};

export const countTerm = function countTerm(lowerText: string, term: string): number {
    let count = 0;
    let from = lowerText.indexOf(term);
    while (from !== -1) {
        const before = from === 0 ? "" : lowerText.charAt(from - 1);
        const after = lowerText.charAt(from + term.length);
        if (isBoundary(before) && isBoundary(after)) {
            count += 1;
        }
        from = lowerText.indexOf(term, from + term.length);
    }
    return count;
};

export const countAny = function countAny(lowerText: string, terms: readonly string[]): number {
    return terms.reduce((sum, term) => sum + countTerm(lowerText, term), 0);
};

const isDigit = function isDigit(char: string): boolean {
    return char >= "0" && char <= "9";
};

const isNumeric = function isNumeric(stem: string): boolean {
    if (stem.length === 0) {
        return false;
    }
    for (const char of stem) {
        if (!isDigit(char) && char !== ".") {
            return false;
        }
    }
    return true;
};

export const isDigitMetric = function isDigitMetric(word: string, units: readonly string[]): boolean {
    const unit = units.find((held) => word.endsWith(held) && word.length > held.length);
    return unit !== undefined && isNumeric(word.slice(0, -unit.length));
};

export const digitMetricsOf = function digitMetricsOf(lowerText: string, units: readonly string[]): number {
    return lowerWordsOf(lowerText).filter((word) => isDigitMetric(word, units)).length;
};
```
