/** * WHAT A SET OF ROWS COMES TO WHEN IT IS FOLDED BY ONE KEY — the arithmetic * behind `GroupRegister`, in its own module because it is the half of that shape * a test can read without rendering anything. * * It imports nothing. The fold is over rows the caller already holds, and every * reading it produces — the groups, a figure over one of them, the order they * are ranked in — is a value, so the component below it renders a result rather * than computing one while it draws. */ /** One row of a group register: the key the rows were folded on, and the set behind it. */ export interface GroupRow { /** * WHAT THE ROWS HAVE IN COMMON — an option's id, a linked record's id, a * period's ISO key. It is the machine's half of the fold and is never drawn: * a register printing `rec_…` where the business says a name is the one defect * a table of aggregates makes invisibly. */ key: string; /** What that key is CALLED — the only half a reader sees. */ label: string; /** The rows behind the figure, in the order they arrived. */ rows: readonly T[]; } /** * HOW THE ROWS OF ONE GROUP BECOME ONE FIGURE. * * `sum` is what a GROUP BY is read for and the default. `min` / `max` answer * "the earliest, the largest" — one row's own value rather than an arithmetic * result — and `average` is the fold a SHARE takes, because shares do not add * up: four rows averaging 85 % sum to 340 %, a figure of no kind at all. */ export type GroupFoldKind = "sum" | "min" | "max" | "average"; /** * THE ROWS, FOLDED — one entry per distinct key, in the order the keys first * appeared. * * ARRIVAL ORDER, never the key's: what the register is ranked by is a separate * decision ({@link rankByFigure}, {@link rankByKey}) and folding in a sorted * order would make the two answers one, silently. A row whose key is empty * still makes a group — "unassigned" is a real answer about the set, and * dropping it would leave the figures short of what the register says it * counted. Its label is the caller's, so the words for it are the screen's. */ export declare function foldRows(rows: readonly T[], by: (row: T) => string, label: (key: string) => string): GroupRow[]; /** * WHAT ONE GROUP'S ROWS COME TO — or `null` where NOT ONE of them states the * figure. * * A sum over nothing is not zero. A group of four orders none of which has been * priced has no total, and drawing it as `0` says the business made nothing * there — the same wrong answer a blank cell tells the truth about. So a stated * figure of zero folds like any other and an unstated one is skipped, and only * a group where every row is silent answers `null`. */ export declare function foldFigure(rows: readonly T[], value: (row: T) => number | null | undefined, kind?: GroupFoldKind): number | null; /** * THE GROUPS RANKED BY THE FIGURE THE REGISTER IS READ FOR, largest first — * which is what a GROUP BY is opened for: who is biggest, where the money is. * * A group whose figure is unstated sorts LAST whatever the direction: it is not * a small group, it is a group nothing is known about, and ranking it as zero * would put it among the ones that really are. Ties break on the KEY so the * order is stable across renders — two customers at the same total must not * swap places when a row lands. */ export declare function rankByFigure(groups: readonly GroupRow[], of: (group: GroupRow) => number | null): GroupRow[]; /** * THE GROUPS IN THEIR KEY'S OWN ORDER — what a register of PERIODS takes, where * the sequence is the subject and ranking by size would destroy it. * * As TEXT, like every other key order in the kit: a key that has to sort is an * ISO date or a zero-padded code, never the words, which sort by their first * letter. */ export declare function rankByKey(groups: readonly GroupRow[], direction: "asc" | "desc"): GroupRow[];