search
import { searchThaiAddress, lookupByZipCode } from 'thaizip'
searchThaiAddress
Section titled “searchThaiAddress”function searchThaiAddress( index: TrigramIndex, query: string, options?: SearchOptions,): ThaiAddressRecord[]Fuzzy address search by subdistrict/district/province name (Thai or English) or postal code. Returns ThaiAddressRecord[] directly — no extra transformation needed before use.
Parameters
Section titled “Parameters”| Name | Type | Default | Description |
|---|---|---|---|
index | TrigramIndex | — (required) | An index built by buildThaiAddressIndex or loadDefaultIndex |
query | string | — (required) | The search text, or a postal code (all digits, at least 2) |
options? | SearchOptions | undefined | Additional options (see table below) |
SearchOptions:
| Name | Type | Default | Description |
|---|---|---|---|
limit? | number | 10 | Caps the number of results — only applies to text queries, not postal-code queries |
threshold? | number | 0.4 | Minimum score (0–1) a result must reach to count as a match — only applies to text queries |
zipLimit? | number | Infinity | Caps the number of results when the query is all digits. Unlimited by default because a single postal code can map to dozens of tambons |
romanizationAliases? | boolean | true | Expands non-RTGS English spellings (e.g. lardprao) to match what’s in the dataset before searching — only applies to Latin-script queries |
Returns
Section titled “Returns”ThaiAddressRecord[], sorted by relevance (see Ranking below). Always returns an empty array [] when nothing matches — never throws.
- Returns
[]immediately ifindexorqueryis empty/falsy, ifquery.length > 1000before normalization, or if the normalized text exceeds 300 characters queryis always run throughnormalizeThaiAddressTextbefore matching (see below)- If the normalized text is all digits (
/^\d+$/) and at least 2 digits long, it’s automatically routed tolookupByZipCode, forwarding onlyzipLimit(limit,threshold, andromanizationAliaseshave no effect on this path) — an all-digit string shorter than 2 digits returns[] - Normalized text shorter than 3 characters (and not a postal code) always returns
[], since trigrams from a string shorter than 3 chars would match meaninglessly - If the normalized query contains a Latin letter (
/[a-z]/i) andromanizationAliases !== false,applyRomanizationAliasesruns before trigram extraction - Ranking happens in three passes, in order: (1)
score = hits / queryTrigrams.sizedescending, (2)matchRankdescending —3when the query exactly matches the tambon’s own name (Thai or English),2for a prefix match,1for a substring match,0when it doesn’t match the tambon’s own name at all (only matched via its parent district/province), (3) Thai-locale sort onprovinceNameTh→amphureNameTh→tambonNameThusing a module-level cachedIntl.Collator('th')(never constructed inside the loop) - For performance, step (3) (the collator tie-break) only runs over the top window of
Math.max(limit * 4, 50)results after a cheap numeric pre-sort on (1)+(2) — not over the full result set
Example
Section titled “Example”import { searchThaiAddress } from 'thaizip'
searchThaiAddress(index, 'ลาดพร้าว', { limit: 5 })searchThaiAddress(index, 'bang rak')searchThaiAddress(index, '10500') // automatically routed to the zip pathlookupByZipCode
Section titled “lookupByZipCode”function lookupByZipCode( index: TrigramIndex, zip: string, options?: SearchOptions,): ThaiAddressRecord[]Looks up records by postal code directly (exact or prefix match). searchThaiAddress calls this automatically when the query is all digits, but you can call it directly too.
Parameters
Section titled “Parameters”| Name | Type | Default | Description |
|---|---|---|---|
index | TrigramIndex | — (required) | The index to search |
zip | string | — (required) | A full or partial postal code. Must be all digits, at least 2 characters long, or [] is returned |
options? | SearchOptions | undefined | Only the zipLimit field is used; the rest are ignored |
Returns
Section titled “Returns”ThaiAddressRecord[], with records whose zipCode exactly matches zip sorted first, followed by prefix matches in ascending order, then truncated to options.zipLimit (default Infinity — not limit)
- The lookup scans every postal code in
index.zipIndexand checks whether it starts withzip(startsWith) — this is always the case, whetherzipis a full 5-digit code or a partial prefix. Its time complexity is therefore always O(total postal codes in the index), never O(1), even for a full code - Results are not capped by
options.limit— onlyzipLimit, since a single postal code can legitimately map to dozens of tambons (e.g.45000maps to 33) - Returns
[]ifindexorzipis empty, or ifzipfails/^\d+$/or is shorter than 2 characters
Example
Section titled “Example”import { lookupByZipCode } from 'thaizip'
lookupByZipCode(index, '45000') // exact match firstlookupByZipCode(index, '450') // prefix scanlookupByZipCode(index, '45000', { zipLimit: 10 })Helper functions
Section titled “Helper functions”These two functions run internally as part of searchThaiAddress’s pipeline and are exported separately in case you need them on their own (for example, normalizing text before storing it, or expanding an alias yourself).
normalizeThaiAddressText
Section titled “normalizeThaiAddressText”function normalizeThaiAddressText(input: string): stringStrips Thai address prefixes from the start of the text — both full-form (จังหวัด/อำเภอ/ตำบล/แขวง/เขต) and abbreviated (จ./อ./ต./ข.) — strips Thai tone marks, then lowercases everything. Used both when building the index (against the data) and when searching (against the query), so both sides end up in the same shape.
| Name | Type | Default | Description |
|---|---|---|---|
input | string | — (required) | The raw text |
Returns: string — the normalized text, or '' if input is empty/falsy
normalizeThaiAddressText('จังหวัดลาดพร้าว') // strips the 'จังหวัด' prefix and tone marksapplyRomanizationAliases
Section titled “applyRomanizationAliases”function applyRomanizationAliases(normalized: string): stringRewrites common non-RTGS English spellings (e.g. lardprao, krungthep) onto the exact RTGS string that actually appears in the dataset, before trigram extraction. Only applies to Latin-script queries (searchThaiAddress calls it automatically when romanizationAliases !== false).
| Name | Type | Default | Description |
|---|---|---|---|
normalized | string | — (required) | Text already run through normalizeThaiAddressText (lowercased, prefixes/tone marks already stripped) |
Returns: string — if normalized matches a key in the alias dictionary, returns the mapped RTGS string immediately. Otherwise it tries stripping a few English administrative words (district, province, changwat, amphoe, amphur, sub-district) and looks the result up again. If it still doesn’t match, returns the original text (or the cleaned-up text, if that differs from the original).
- Must be a pure function — it runs on every keystroke
- Thai-script text is returned unchanged
- A few aliases deliberately map onto typos that actually exist in the dataset (
loburi,buogkan), because that’s what’s really indexed
applyRomanizationAliases('lardprao') // 'lat phrao'applyRomanizationAliases('ลาดพร้าว') // 'ลาดพร้าว' (Thai text is untouched)