# core/aggregators/field.aggregator.ts

> 56 lines of code and 13 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-aggregators-field-aggregator-ts
Source text: https://banes-lab.com/source/patterns/core/aggregators/field.aggregator.ts.txt

Listed in [core/aggregators](https://banes-lab.com/api/source/patterns/core/aggregators.md), before [core/aggregators/representation.distribution.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.distribution.aggregator.ts.md).

## Definitions

- `result` (method_definition, line 28, exported)
- `observe` (method_definition, line 13, exported)
- `FieldAccumulator` (class_declaration, line 5, exported)
- `primitives` (public_field_definition, line 7, exported)
- `lengths` (public_field_definition, line 8, exported)
- `present` (public_field_definition, line 9, exported)
- `elementNumeric` (public_field_definition, line 10, exported)
- `sawElement` (public_field_definition, line 11, exported)
- `kind` (lexical_declaration, line 18, exported)
- `kinds` (lexical_declaration, line 33, exported)
- `[kind]` (lexical_declaration, line 36, exported)
- `[firstLength]` (lexical_declaration, line 40, exported)
- `observeList` (method_definition, line 51, exported)

## Contained in

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

## Uses

- [configuration/strings/schema.strings.ts](https://banes-lab.com/source/patterns/configuration/strings/schema.strings.ts.md)
- [core/classifiers/schema.classifier.ts](https://banes-lab.com/source/patterns/core/classifiers/schema.classifier.ts.md)

## Linked from

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

## Source

```typescript
import { DataLoadError, isBlank, kindOf, primitiveOf, resolvePrimitive } from "#core/classifiers/schema.classifier";
import type { FieldSchema, Kind, Primitive } from "#types/schema.types";
import { heterogeneousKinds, unresolvedKind } from "#configuration/strings/schema.strings";

export class FieldAccumulator {
    private readonly kinds = new Set<Kind>();
    private readonly primitives = new Set<Primitive>();
    private readonly lengths = new Set<number>();
    private present = 0;
    private elementNumeric = true;
    private sawElement = false;

    public observe(value: unknown, floatField: boolean): void {
        if (isBlank(value)) {
            return;
        }
        this.present += 1;
        const kind = kindOf(value);
        this.kinds.add(kind);
        if (kind === "scalar") {
            this.primitives.add(primitiveOf(value, floatField));
        }
        if (Array.isArray(value)) {
            this.observeList(value);
        }
    }

    public result(name: string, total: number): FieldSchema {
        if (this.present === 0) {
            return { elementNumeric: false, fixedLength: null, kind: "scalar", name, nullable: true, primitive: null };
        }
        if (this.kinds.size !== 1) {
            const kinds = [...this.kinds].sort((a, b) => a.localeCompare(b)).join(", ");
            throw new DataLoadError(heterogeneousKinds(name, kinds));
        }
        const [kind] = [...this.kinds];
        if (kind === undefined) {
            throw new DataLoadError(unresolvedKind(name));
        }
        const [firstLength] = [...this.lengths];
        return {
            elementNumeric: this.sawElement && this.elementNumeric,
            fixedLength: this.lengths.size === 1 ? (firstLength ?? null) : null,
            kind,
            name,
            nullable: this.present < total,
            primitive: kind === "scalar" ? resolvePrimitive(name, this.primitives) : null,
        };
    }

    private observeList(value: readonly unknown[]): void {
        this.lengths.add(value.length);
        for (const element of value) {
            this.sawElement = true;
            if (typeof element !== "number") {
                this.elementNumeric = false;
            }
        }
    }
}
```
