# columns and roles - what a column holds, and what it is Source: https://truecopy.dev/docs/columns-and-roles/ Module: truecopy/columns · truecopy/roles Count what each column contains once, then deduce what it is from that. Recognising a header label only works on issuers you have already seen. ## columns, counted once ```ts import { profileColumns, dominantKind } from 'truecopy/columns'; const profiles = profileColumns(rows, { kindOf, sampleLimit: 200 }); // [{ shareOfKind: { date: 0.83, text: 0.17 }, shareFilled: 0.83 }, …] ``` One pass, two uses, and they were written twice before this existed: a reader infers a column's **role** from its content, and elsewhere learns the type **signature** of the table to drop the rows that break it. Both start by counting the same thing. The kind of a cell is a plain string, named by you: an enum here would mean this library had an opinion on what documents contain, and it has none. **`sampleLimit` (200 by default).** A table describes itself in its first rows; reading ten thousand to learn what sixty already said is time somebody spends watching a spinner. **`shareOfKind` is divided by ALL rows**, not by the filled ones. A column of dates with a third of its cells empty is not a column of dates: it is a mess, and judging rows against it would condemn the honest ones. ## roles, deduced from that ```ts import { assignRoles } from 'truecopy/roles'; const roles = assignRoles(profiles, [ { role: 'date', kind: 'date', minimum: 0.5, take: 'best', among: 'filled' }, { role: 'money', kind: 'amount', minimum: 0.5, take: 'each', among: 'filled' }, { role: 'description', kind: 'text', minimum: 0.4, take: 'best', among: 'filled' } ]); // ['date', 'description', undefined, 'money', undefined] ``` Recognising a header label only works on issuers whose labels you have already seen. Reading the **content** works on the next one. ### Order is the only precedence there is A column already named is **out of the running** for what comes after. That is how _"the wordiest column among those left is the description"_ gets stated, a rule that reads backwards if the description is looked for first. ### take - `best`: the single strongest column, and only it. A tie resolves on the **leftmost**: a table is read left to right, and so is the tie. - `each`: every column that qualifies. What several of them mean is yours: one money column is a signed amount, two are a debit and a credit, and that is domain knowledge no library can be told. ### among, and this one costs if you get it wrong | | out of | | ------------------ | ----------------------------------------------------------------------------- | | `rows` _(default)_ | every row. The cautious reading, and what [signature](https://truecopy.dev/docs/signature/) needs. | | `filled` | the cells that are actually there. | A statement leaves the date cell empty on every continuation line of a description that wrapped, and **that column is still the date column**: the question here is not whether the column is reliable, it is what is in it. Measured on a real statement: a date column filled on 26 rows out of 60, of which 16 were dates. That is **0.27** out of the rows and **0.62** out of the filled ones. Past a threshold of a half, the first answer loses the column outright, and with it every role that depended on it. ### undefined, never a filler name A column whose role is unknown must be able to say so, because every rule downstream is entitled to refuse to judge on it. Naming it `'unknown'` invites code to treat that as a role. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt