← back to Rentv 2026
fix(deals): two location misparses — trust explicit title state over body-leaked city (AZ deal no longer labeled CA) + stop 'in <City>. <Word>' swallowing a next-sentence word (Sunnyvale. Located → Sunnyvale/CA)
d088ba4ffdb0cf52f0afb2a7d03774cc09bac0a0 · 2026-08-06 05:56:55 -0700 · Steve
Files touched
M scripts/lib/parse-location.mjs
Diff
commit d088ba4ffdb0cf52f0afb2a7d03774cc09bac0a0
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Aug 6 05:56:55 2026 -0700
fix(deals): two location misparses — trust explicit title state over body-leaked city (AZ deal no longer labeled CA) + stop 'in <City>. <Word>' swallowing a next-sentence word (Sunnyvale. Located → Sunnyvale/CA)
---
scripts/lib/parse-location.mjs | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/scripts/lib/parse-location.mjs b/scripts/lib/parse-location.mjs
index b28f337c..ad58838f 100644
--- a/scripts/lib/parse-location.mjs
+++ b/scripts/lib/parse-location.mjs
@@ -24,7 +24,12 @@ const clean = (s) => (s || '').trim().replace(/[’']s$/, '').replace(/[.,]$/, '
const NOT_CITY = /^(Retail|Office|Industrial|Multifamily|Mixed|Res|Residential|Apartment|Apartments|Distribution|Warehouse|Logistics|Medical|Hotel|Self|Portfolio|Property|Properties|Community|Center|Tower|Plaza|Ranch|Facility|Trio|Pac|New|Downtown|The|A|An|This|That|Its|His|Her|Their|Two|Three|Both|North|South|East|West|Northwest|Southwest|Northeast|Southeast|Pacific|Greater)$/i;
// Strip trailing non-place words a capture may have swallowed ("Oceanside Res" → "Oceanside").
const tidy = (c) => {
- c = clean(c).replace(/\s+(Res|Metro|Area|Region|Residential|Portfolio|Community|Property|Properties|Industrial|Office|Retail|Facility|Facilities|Holdings|Assets|Buy|Sale|Deal)$/i, '');
+ c = clean(c)
+ // Drop a next-SENTENCE word the capture swallowed across a period ("Sunnyvale. Located" → "Sunnyvale").
+ // The "in <City>" / lead char class allows '.', so a sentence-ending period + the following capitalized
+ // word (e.g. "…in Sunnyvale. Located at 1200 Kifer Rd…") got glued into a bogus two-word city.
+ .replace(/\.\s+\S.*$/, '')
+ .replace(/\s+(Res|Metro|Area|Region|Residential|Portfolio|Community|Property|Properties|Industrial|Office|Retail|Facility|Facilities|Holdings|Assets|Buy|Sale|Deal)$/i, '');
const first = (c.split(' ')[0] || '');
return (!c || c.length < 3 || FIRM_ADJ.test(c) || NOT_CITY.test(first) || NOT_CITY.test(c)) ? null : c;
};
@@ -43,9 +48,26 @@ const CITY_STATE = {
const STATE_OF = {};
for (const [st, cities] of Object.entries(CITY_STATE)) for (const c of cities) STATE_OF[c.toLowerCase()] = st;
+// Unambiguous 2-letter state abbreviations that reliably mean THE STATE in a CRE headline. Full
+// names are deliberately excluded — "Colorado"/"Washington"/"Oregon" double as street/place names
+// (Colorado Blvd, Washington St), so they'd mis-fire; two-cap standalone tokens don't.
+const TITLE_ST = /\b(AZ|CA|NV|TX|CO|OR|WA|NM|UT|ID)\b/;
+
export function parseLocation(title = '', body = '') {
const r = _parseLocation(title, body);
if (r.city && !r.state) { const s = STATE_OF[r.city.toLowerCase()]; if (s) r.state = s; }
+ // The headline is asset-about (parser rule 1). When the TITLE names an explicit state but the
+ // location we resolved lands in a DIFFERENT state, the city was leaked from the body (a firm HQ,
+ // a comp, or another market the buyer plays in) — e.g. "…Closes $34.6 Mil AZ Industrial Buy" whose
+ // body mentions the Inland Empire. Trust the title's state so CA/AZ rollups aren't corrupted; drop
+ // the leaked city (it isn't the asset). We compare the RESOLVED r.state (not the lossy STATE_OF map),
+ // so a correctly-paired "Glendale, AZ" — already state:AZ — agrees with the title and is left intact.
+ const tm = title.match(TITLE_ST);
+ if (tm) {
+ const titleSt = tm[1];
+ if (!r.state) r.state = titleSt;
+ else if (r.state !== titleSt) { r.city = null; r.state = titleSt; }
+ }
return r;
}
← b0c8d34c auto-save: 2026-08-06T05:48:58 (7 files) — data/deals-regist
·
back to Rentv 2026
·
harden(deals): Cody gate — lookbehind so period-cut spares S db6ec30d →