> ## Documentation Index
> Fetch the complete documentation index at: https://docs.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Read a filed table

> Copy a rate table from the filing into a rater, read it in a calculation, and handle the keys the filing leaves out.

[Install Relativities and download the synthetic examples](/relativities/setup) before running this page.

This page copies two filed tables into a rater and reads them. A base rate table has two keys,
construction and protection class. A territory factor table lists some territories, plus one
factor for all the others.

The check proves that each lookup finds a row for every request the rules accept. The last
sections show what to do when the filing leaves out some keys.

## Declare the tables

```rsl theme={null}
Types = {
    Construction = {
        frame,
        masonry
    },
    ProtectionClass = IntBands(1, 4, 7, 10..),
    Territory = Code(pattern: "[0-9]{3}")
}

Tables = {
    BaseRate [cite: manual:p3] :
        table dims (construction: Construction, protection: ProtectionClass) -> {
            rate: Rate
        }
        columns: {
            construction: "const",
            protection: "pc",
            rate: "base_rate"
        },
    TerritoryFactor [cite: manual:p4] :
        table dims (territory: Territory) -> {
            factor: Factor
        }
        otherwise [cite: manual:p4]: { factor: 1.25 }
}
```

* `dims` names the key columns. A key is an enum, a band type, a `Code` or `Bool`.
* The names after `->` are the output columns, such as a rate or a factor.
* The citation after the table name points to the page that prints the table.
* `columns` maps a column name to a CSV header with a different name.
* `Territory` is a **code**: a three-digit string. `Code` accepts any string that fits the pattern.
* `otherwise` gives the factor for each territory the CSV leaves out.

## Write the rows

Each table reads its rows from `tables/` plus the table name and `.csv`. `tables/BaseRate.csv`:

```csv theme={null}
const,pc,base_rate
frame,1,2.40
frame,4,2.90
frame,7,3.70
frame,10,5.60
masonry,1,2.10
masonry,4,2.60
masonry,7,3.40
masonry,10,5.20
```

`tables/TerritoryFactor.csv`:

```csv theme={null}
territory,factor
101,0.95
102,1.00
103,1.10
```

* An enum cell holds the member's name, as a request sends it.
* A band cell holds the band's first value, such as `4` for the band 4 to 6.
* A number cell holds digits with an optional decimal point.

## Read the tables

`lookup!` takes the table and one key for each key column, in order. Read an output column after
the dot:

```rsl theme={null}
            base: Rate [cite: manual:p3] = lookup!(BaseRate, self.construction, self.protection_class).rate,
            territory_factor: Factor [cite: manual:p4] = lookup!(TerritoryFactor, self.territory).factor,
            premium: Premium [cite: manual:p9] =
                round(self.coverage_a / 1000 * self.base * self.territory_factor, 0, half_up)
```

Rate a frame dwelling of 200,000 in protection class 5, first in territory 101 and then in 999.
The sample download includes this rater in `relativities-examples/rsl/table-otherwise`:

```python theme={null}
import json

import rslc

rater = rslc.PreparedRater("relativities-examples/rsl/table-otherwise")
for territory in ["101", "999"]:
    policy = {"Policy": {"construction": "frame", "protection_class": 5, "territory": territory, "coverage_a": 200000}}
    print(rater.rate(json.dumps(policy)))
# {"ok":true,"outputs":{"Policy":{"premium":551}}}
# {"ok":true,"outputs":{"Policy":{"premium":725}}}
```

Territory 999 is missing from the CSV, so it takes the `otherwise` factor of 1.25.

## Handle the keys the filing leaves out

Each lookup has to find a row. Take out the `otherwise` row, and territory 999 lacks a row. The check
stops:

```text theme={null}
FAIL rater.rsl (strict)
error: E-LOOKUP-GAP rater.rsl:51:58: this lookup of `TerritoryFactor` can reach (territory: a code that no row names; the `Territory` domain is open), but `TerritoryFactor` has no row for it. A lookup's key type must exclude every absent row: key `territory` has an open domain, so the table needs `otherwise`: when the filing has an all-other row, add `otherwise [cite: source:region]: { … }`; otherwise narrow the input to the filed codes (an enum of the filed codes, or a refinement with `[code: "CODE", message: "…"]`)
cost premium: 5 (each list of size 1: 5, 3: 5, 10: 5)
```

Choose the fix that matches the filing:

| The filing | The fix |
| - | - |
| prints a value for all other keys | add a cited `otherwise` row |
| rates only the keys it lists | type the input `Key(T)`, as the next section shows |
| refuses some keys by a rule | add the rule to the input, with its code |
| uses bands | match the input range to the bands, or add an open band |

## Accept only the keys the table lists

`Key(TerritoryFactor)` accepts only the territories that `TerritoryFactor` lists. The rater refuses
any other territory before it calculates:

```rsl theme={null}
            territory: Key(TerritoryFactor),
```

The table then needs only its filed rows. The sample download includes this version in
`relativities-examples/rsl/table-key`:

```python theme={null}
rater = rslc.PreparedRater("relativities-examples/rsl/table-key")
for territory in ["101", "999"]:
    policy = {"Policy": {"construction": "frame", "protection_class": 5, "territory": territory, "coverage_a": 200000}}
    print(rater.rate(json.dumps(policy)))
# {"ok":true,"outputs":{"Policy":{"premium":551}}}
# {"ok":false,"refusal":{"error":"InvalidInputs","truncated":false,"violations":[{"cite":null,"code":"INPUT-KEY-UNFILED","constraint":"territory: Key(TerritoryFactor)","message":"territory does not name a filed row of TerritoryFactor","path":"Policy.territory","value":"999"}]}}
```

`Key(T)` works on a table with one key column, of a `Code` or enum type.

## Mark a row the filing leaves blank

Some filings print a row with a blank value, such as a log home that lacks a construction factor.
Write `unfiled` in that cell:

```csv theme={null}
construction,factor
frame,1.20
masonry,1.00
log,unfiled
```

A lookup that can reach that row fails the check:

```text theme={null}
FAIL rater.rsl (strict)
error: E-LOOKUP-GAP rater.rsl:39:52: this lookup of `ConstructionFactor` can reach (construction: log), but CSV row 4 of `ConstructionFactor` is `unfiled`. A lookup's key type must exclude every absent row: narrow the `construction` key so it cannot be `log`: type the input it reads `Construction - { log }`, or handle `log` in its own `match` arm that does not look up `ConstructionFactor`; or key the lookup by an identity enum whose mapping table lists only filed tuples (for example `VehicleIdentity` filled by `VehicleIdentityMap`); or add the filed row to `ConstructionFactor`; or when the filing has an all-other row, add `otherwise [cite: source:region]: { … }`
cost premium: 2 (each list of size 1: 2, 3: 2, 10: 2)
```

Add the filing's rule to the input, in place of the `Construction - { log }` form the message
suggests. The rater then refuses a log home with that rule's code:

```rsl theme={null}
            construction: Construction
                where self in {@Construction.frame, @Construction.masonry} [code: "LOG-HOMES", message: "Log homes are rated by referral.", cite: manual:p5],
```

`Key(ConstructionFactor)` also works here. It refuses a log home with `INPUT-KEY-UNFILED`.

## Related

* [What rslc checks and guarantees](/relativities/rsl/explanation/checks-and-guarantees) shows the band key message, `E-KEY-RANGE`.
* [RSL at a glance](/relativities/rsl/language-tour) reads a table with one rate per peril.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.