# core/selectors/grammar.selector.ts

> 41 lines of code and 8 definitions.

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

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

## Definitions

- `roleIdOf` (lexical_declaration, line 8, exported)
- `hasRoleIn` (lexical_declaration, line 12, exported)
- `categoriesOf` (lexical_declaration, line 16, exported)
- `contextFor` (lexical_declaration, line 32, exported)
- `keywordIdOf` (lexical_declaration, line 4, exported)
- `constructGroundsOf` (lexical_declaration, line 22)
- `byCategory` (lexical_declaration, line 23)
- `documentType` (lexical_declaration, line 33, exported)

## Contained in

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

## Used by

- [core/factories/grammar.factory.ts](https://banes-lab.com/source/context/core/factories/grammar.factory.ts.md)
- [core/validators/grammar.reference.validator.ts](https://banes-lab.com/source/context/core/validators/grammar.reference.validator.ts.md)
- [core/validators/grammar.validator.ts](https://banes-lab.com/source/context/core/validators/grammar.validator.ts.md)

## Linked from

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

## Source

```typescript
import type { ContextForResult, GrammarContext, KeywordRecord, KeywordRole, PagData } from "#types/grammar.types";
import { KEYWORD_SEPARATOR, REQUIRED_SECTIONS } from "#configuration/constants/grammar.constants";

export const keywordIdOf = function keywordIdOf(record: KeywordRecord): string {
    return record.keyword;
};

export const roleIdOf = function roleIdOf(record: KeywordRecord, role: KeywordRole): string {
    return `${role.category}${KEYWORD_SEPARATOR}${record.keyword}`;
};

export const hasRoleIn = function hasRoleIn(record: KeywordRecord, category: string): boolean {
    return record.roles.some((role) => role.category === category);
};

export const categoriesOf = function categoriesOf(data: PagData): string[] {
    return [...new Set(data.keywords.flatMap((record) => record.roles.map((role) => role.category)))].toSorted((a, b) =>
        a.localeCompare(b),
    );
};

const constructGroundsOf = function constructGroundsOf(data: PagData): Record<string, string[]> {
    const byCategory = new Map<string, Set<string>>();
    for (const role of data.keywords.flatMap((record) => record.roles)) {
        byCategory.set(role.category, new Set([...(byCategory.get(role.category) ?? []), ...(role.grounds ?? [])]));
    }
    return Object.fromEntries(
        [...byCategory].map(([category, set]) => [category, [...set].toSorted((a, b) => a.localeCompare(b))]),
    );
};

export const contextFor = function contextFor(type: string, ctx: GrammarContext): ContextForResult {
    const documentType = ctx.indexes.typeByName.get(type) ?? null;
    return {
        applicable: documentType !== null,
        contracts: documentType && ctx.resolver.contractsForType ? ctx.resolver.contractsForType(type) : [],
        documentType,
        grounding: {
            axis: documentType?.axis ?? null,
            constructGrounds: constructGroundsOf(ctx.data),
            model: documentType?.model ?? null,
        },
        palette: { constructs: categoriesOf(ctx.data), verbs: documentType ? [...documentType.verbs] : [] },
        requiredSections: [...REQUIRED_SECTIONS],
        template: ctx.indexes.templateByType.get(type) ?? null,
    };
};
```
