thi-ng/umbrella

View on GitHub
packages/transducers/src/struct.ts

Summary

Maintainability
A
0 mins
Test Coverage
import type { Fn } from "@thi.ng/api";
import { isIterable } from "@thi.ng/checks/is-iterable";
import type { Transducer } from "./api.js";
import { comp } from "./comp.js";
import { iterator } from "./iterator.js";
import { mapKeys } from "./map-keys.js";
import { partitionOf } from "./partition-of.js";
import { partition } from "./partition.js";
import { rename } from "./rename.js";

/**
 * Structfield definition: [name, number-of-inputs, transform?]
 */
export type StructField<T> = [string, number, Fn<T[], any>?];

/**
 * Higher-order transducer to converts linear input into structured
 * objects using given field specs and ordering.
 *
 * @remarks
 * A single field spec is an array of 2 or 3 items:
 *
 * `[name, size, transform?]`.
 *
 * If `transform` is given, it will be used to produce the final value
 * for this field. In the example below, it is used to unwrap the ID
 * field values, e.g. from `[123] => 123`
 *
 * @example
 * ```ts
 * import { push, struct, transduce } from "@thi.ng/transducers";
 *
 * transduce(
 *     struct([["id", 1, (id) => id[0]], ["pos", 2], ["vel", 2], ["color", 4]]),
 *     push(),
 *     [0, 100, 200, -1, 0, 1, 0.5, 0, 1, 1, 0, 0, 5, 4, 0, 0, 1, 1]
 * )
 * // [ { color: [ 1, 0.5, 0, 1 ],
 * //     vel: [ -1, 0 ],
 * //     pos: [ 100, 200 ],
 * //     id: 0 },
 * //   { color: [ 0, 0, 1, 1 ],
 * //     vel: [ 5, 4 ],
 * //     pos: [ 0, 0 ],
 * //     id: 1 } ]
 * ```
 *
 * @param fields -
 */
export function struct<A, B>(fields: StructField<A>[]): Transducer<A, B>;
export function struct<A, B>(
    fields: StructField<A>[],
    src: Iterable<A>
): IterableIterator<B>;
export function struct(fields: StructField<any>[], src?: Iterable<any>): any {
    return isIterable(src)
        ? iterator(struct(fields), src)
        : comp(
                partitionOf(fields.map((f) => f[1])),
                partition(fields.length),
                rename(fields.map((f) => f[0])),
                mapKeys(
                    fields.reduce(
                        (acc: any, f) =>
                            f[2] ? ((acc[f[0]] = f[2]), acc) : acc,
                        {}
                    ),
                    false
                )
          );
}