UNPKG

extract-us-city

Version:

A library that extracts City, State and Zip information from text and provides a structured response.

1 lines 3.4 kB
const fs=require("fs"),path=require("path");let database,citiesArray;const loadDatabase=()=>{const t=fs.readFileSync(path.resolve(__dirname,"city-db.json"));database||(database=JSON.parse(t)),citiesArray||(citiesArray=database.map(t=>{if(t.city.indexOf("-")>-1)return t.city.replace("-"," ");if(t.city.indexOf("'")>-1){return t.city.replace("'"," ").replace(" "," ")}return t.city.indexOf(".")>-1?t.city.replace(".",""):t.city}))},tokenize=t=>{const e=[],a=/\w+/g;let r,d=0;for(;null!==(r=a.exec(t));)r[0].charAt(0)!==r[0].charAt(0).toUpperCase()&&"of"!==r[0]&&"the"!==r[0]&&"del"!==r[0]&&"s"!==r[0]||(e.push({word:r[0],startCharIndex:r.index,endCharIndex:a.lastIndex,firstWordIndex:d,lastWordIndex:d}),d++);return e},toTitleCase=t=>t.charAt(0)===t.charAt(0).toUpperCase()&&t.charAt(1)===t.charAt(1).toUpperCase()&&isNaN(t)?t.replace(/\w\S*/g,t=>"of"!==t&&"the"!==t&&"del"!==t?t.charAt(0).toUpperCase()+t.substr(1).toLowerCase():t):t,identifyFullCity=(t,e)=>{let a;citiesArray.indexOf(toTitleCase(e.word))>-1&&(a=e);for(let r=1;r<6;r++){let d=e.word;for(let a=1;a<=r;a++)t[e.firstWordIndex+a]&&(d+=` ${t[e.firstWordIndex+a].word}`);citiesArray.indexOf(toTitleCase(d))>-1&&(a={word:d,startCharIndex:e.startCharIndex,endCharIndex:t[e.firstWordIndex+r].endCharIndex,firstWordIndex:e.firstWordIndex,lastWordIndex:t[e.firstWordIndex+r].lastWordIndex})}return a},findStateMatches=(t,e)=>{const a=[];return e.forEach((e,r)=>{t.word!==e.state_code&&t.word!==e.state_name||a.push({matchIndex:r,...t})}),a},findZipMatches=(t,e)=>{const a=[];return e.forEach((e,r)=>{e.zips.split(" ").forEach(e=>{t.word===e&&a.push({matchIndex:r,zip:e,...t})})}),a},identifyNearByData=(t,e)=>{const a=[],r=toTitleCase(e.word);let d,n;citiesArray.forEach((t,e)=>{t===r&&a.push(Object.assign({},database[e]))});for(let r=1;r<6;r++){const n=e.lastWordIndex+r;if("left"!==d&&t[n]){const e=findStateMatches(t[n],a);e.length>0&&(d="right",e.forEach(t=>{a[t.matchIndex].foundState=!0,a[t.matchIndex].end=t.endCharIndex,a[t.matchIndex].lastWordIndex=t.lastWordIndex}));const r=findZipMatches(t[n],a);0===e.length&&r.length>0&&(d="right",r.forEach(t=>{a[t.matchIndex].foundZip=!0,a[t.matchIndex].zip=t.zip,a[t.matchIndex].end=t.endCharIndex,a[t.matchIndex].lastWordIndex=t.lastWordIndex}))}const s=e.firstWordIndex-r;if("right"!==d&&t[s]){const e=findStateMatches(t[s],a);e.length>0&&(d="left",e.forEach(t=>{a[t.matchIndex].foundState=!0,a[t.matchIndex].start=t.startCharIndex}));const r=findZipMatches(t[s],a);0===e.length&&r.length>0&&(d="left",r.forEach(t=>{a[t.matchIndex].foundZip=!0,a[t.matchIndex].zip=t.zip,a[t.matchIndex].start=t.startCharIndex}))}}let s=!1;return a.forEach(t=>{t.foundState&&t.foundZip?(n=Object.assign({},t),s=!0):(t.foundState&&!s||t.foundZip&&!s)&&(n=Object.assign({},t))}),n&&(n.zip||1!==n.zips.split("").length||(n.zip=String(n.zips)),n.zip&&delete n.zips,n.foundState||(n.foundState=!1),n.foundZip||(n.foundZip=!1),n.start||(n.start=e.startCharIndex),n.end||(n.end=e.endCharIndex),n.lastWordIndex||(n.lastWordIndex=e.lastWordIndex)),n},extract=t=>{loadDatabase();const e=tokenize(t),a=[];for(let t=0;t<e.length;t++){const r=e[t],d=toTitleCase(r.word);let n=!1;citiesArray.forEach(s=>{if(s===d&&!n||s.startsWith(d)&&!n){n=!0;const d=identifyFullCity(e,r);if(d){const r=identifyNearByData(e,d);r&&(t=Number(r.lastWordIndex),delete r.lastWordIndex,a.push(r))}}})}return a};module.exports={extract:extract};