Created
September 26, 2019 04:55
-
-
Save nalbion/5097eb072c97204c87b3878b129f6fb8 to your computer and use it in GitHub Desktop.
Parse a formattedAddress
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'mocha'; | |
import {expect} from 'chai'; | |
import parseFormattedAddress from './parse-formatted-address'; | |
describe('parseFormattedAddress', () => { | |
it('supports addresses without country', () => { | |
const place = parseFormattedAddress('100 Best Road, Seven Hills NSW'); | |
expect(place.address).to.equal('100 Best Road'); | |
expect(place.city).to.equal('Seven Hills'); | |
expect(place.state).to.equal('NSW'); | |
}); | |
it('supports Australian suburbs', () => { | |
const place = parseFormattedAddress('Rouse Hill NSW 2155, Australia'); | |
expect(place.city).to.equal('Rouse Hill'); | |
expect(place.state).to.equal('NSW'); | |
expect(place.postalCode).to.equal('2155'); | |
expect(place.country).to.equal('Australia'); | |
expect(place.countryCode).to.equal('au'); | |
}); | |
it('supports UK postcodes', () => { | |
const place = parseFormattedAddress('12 Taverner Cl, Basingstoke RG21 4JD, UK'); | |
expect(place.address).to.equal('12 Taverner Cl'); | |
expect(place.city).to.equal('Basingstoke'); | |
expect(place.postalCode).to.equal('RG21 4JD'); | |
expect(place.country).to.equal('United Kingdom'); | |
expect(place.countryCode).to.equal('uk'); | |
}); | |
it('supports USA addresses', () => { | |
const place = parseFormattedAddress('6 West 49th Street, New York, NY, USA'); | |
expect(place.address).to.equal('6 West 49th Street'); | |
expect(place.city).to.equal('New York'); | |
expect(place.country).to.equal('United States'); | |
expect(place.countryCode).to.equal('us'); | |
}); | |
it('supports Indian addresses', () => { | |
const place = parseFormattedAddress('SCF - 96, Main Market Road, Sector 6, Karnal, Haryana 132001, India'); | |
expect(place.address).to.equal('SCF - 96, Main Market Road, Sector 6'); | |
expect(place.city).to.equal('Karnal'); | |
expect(place.postalCode).to.equal('132001'); | |
expect(place.country).to.equal('India'); | |
expect(place.countryCode).to.equal('in'); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Address { | |
address?: string; | |
city: string; | |
state?: string; | |
postalCode?: string; | |
country?: string; | |
countryCode?: string; | |
} | |
/** | |
* Adapted from https://imbibe.in/blog/2017/03/10/parsing-address-components-google-maps-geocoding-api-responses/ | |
* @param address | |
*/ | |
export default function parseFormattedAddress(formattedAddress: string): Address { | |
const address: Address = { | |
city: '' | |
}; | |
if(!formattedAddress) { | |
return address; | |
} | |
let parts = formattedAddress.split(',').map(part => part.trim()); | |
let i = parts.length - 1; | |
// Generally the country is the last part of the address (or absent) | |
if (i >= 0) { | |
const part = fnParsePostalCode(parts[i], address); | |
if (part) { | |
address.country = part; | |
if (address.country === 'USA') { | |
address.countryCode = 'us'; | |
address.country = getCountryName(address.countryCode); | |
} else if (address.country.length === 2) { | |
address.countryCode = address.country.toLowerCase(); | |
address.country = getCountryName(address.countryCode); | |
} else { | |
address.countryCode = getCountryCode(address.country); | |
if (!address.countryCode) { | |
// it's probably not a country | |
address.country = undefined; | |
i++; | |
} | |
} | |
} | |
i--; | |
} | |
// Attempt to parse the state & postal code | |
if (i >= 0) { | |
let part = fnParsePostalCode(parts[i], address); | |
if (part) { | |
// some times the city and state are not separated by a comma | |
const cityAndState = part.match(/(.+) ([A-Z]+)/); | |
if (cityAndState) { | |
part = cityAndState[2]; | |
parts[i] = cityAndState[1]; | |
i++; | |
} | |
address.state = part; | |
} | |
i--; | |
} | |
if (i >= 0) { | |
const part = fnParsePostalCode(parts[i], address); | |
if (part) { | |
address.city = part; | |
} | |
i--; | |
} | |
if (i >= 0) { | |
parts = parts.slice(0, i + 1); | |
address.address = parts.join(', '); | |
} else if (address.countryCode === 'uk' && address.state) { | |
console.info('UK address with state but no street address? I don\'t think so:', JSON.stringify(address)); | |
address.address = address.city; | |
address.city = address.state; | |
address.state = undefined; | |
} | |
console.info(`parsed '${formattedAddress}' -> ${JSON.stringify(address)}`); | |
return address; | |
} | |
function fnParsePostalCode(value: string, address: Address) { | |
const match = value.match(getPostalCodeRegEx(address.countryCode)); | |
if (match) { | |
address.postalCode = match[2]; | |
return match[1].trim(); | |
} else { | |
return value; | |
} | |
} | |
function getPostalCodeRegEx(countryCode: string) { | |
return { | |
ca: /(.*)\b(?![DFIOQU])([A-VXY][0-9][A-Z] ?[0-9][A-Z][0-9])$/, | |
uk: /(.*)\b([A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-HJLNP-UW-Z]{2})$/, | |
us: /(.*)\b([0-9]{5}(?:-[0-9]{4}))?$/ | |
}[countryCode] || /(.*)\b(\d+)$/; | |
} | |
export function getCountryCode(country: string | {name: string; 'alpha-2': string; 'alpha-3': string;}) { | |
if (typeof country === 'string') { | |
country = country.toLowerCase(); | |
if (country.length === 2) { | |
return country; | |
} | |
return { | |
'antarctica': 'aq', | |
'argentina': 'ar', | |
'australia': 'au', | |
'austria': 'at', | |
'bahrain': 'bh', | |
'belgium': 'be', | |
'brazil': 'br', | |
'canada': 'ca', | |
'chile': 'cl', | |
'china': 'cn', | |
'colombia': 'co', | |
'costa rica': 'cr', | |
'czech republic': 'cz', | |
'denmark': 'dk', | |
'ecuador': 'ec', | |
'egypt': 'eg', | |
'finland': 'fi', | |
'france': 'fr', | |
'germany': 'de', | |
'greece': 'gr', | |
'hong kong': 'hk', | |
'hungary': 'hu', | |
'india': 'in', | |
'indonesia': 'id', | |
'ireland': 'ie', | |
'israel': 'il', | |
'italy': 'it', | |
'japan': 'jp', | |
'kuwait': 'kw', | |
'luxembourg': 'lu', | |
'malaysia': 'my', | |
'mexico': 'mx', | |
'morocco': 'ma', | |
'netherlands': 'nl', | |
'new zealand': 'nz', | |
'nigeria': 'ng', | |
'norway': 'no', | |
'oman': 'om', | |
'pakistan': 'pk', | |
'panama': 'pa', | |
'peru': 'pe', | |
'philippines': 'ph', | |
'poland': 'pl', | |
'portugal': 'pt', | |
'qatar': 'qa', | |
'romania': 'ro', | |
'russia': 'ru', | |
'saudi arabia': 'sa', | |
'singapore': 'sg', | |
'south africa': 'za', | |
'south korea': 'kr', | |
'spain': 'es', | |
'sweden': 'se', | |
'switzerland': 'ch', | |
'taiwan': 'tw', | |
'thailand': 'th', | |
'turkey': 'tr', | |
'ukraine': 'ua', | |
'united arab emirates': 'ae', | |
'united kingdom': 'gb', | |
'united states': 'us', | |
'uruguay': 'uy', | |
'venezuela': 've', | |
'vietnam': 'vn', | |
// Aliases | |
'england': 'gb', | |
'america': 'us' | |
}[country]; | |
} else { | |
return country['alpha-2'].toLowerCase(); | |
} | |
} | |
export function getCountryName(countryCode: string) { | |
return { | |
'aq': 'Antarctica', | |
'ar': 'Argentina', | |
'au': 'Australia', | |
'at': 'Austria', | |
'bh': 'Bahrain', | |
'be': 'Belgium', | |
'br': 'Brazil', | |
'ca': 'Canada', | |
'cl': 'Chile', | |
'cn': 'China', | |
'co': 'Colombia', | |
'cr': 'Costa Rica', | |
'cz': 'Czech Republic', | |
'dk': 'Denmark', | |
'ec': 'Ecuador', | |
'eg': 'Egypt', | |
'fi': 'Finland', | |
'fr': 'France', | |
'de': 'Germany', | |
'gr': 'Greece', | |
'hk': 'Hong Kong', | |
'hu': 'Hungary', | |
'in': 'India', | |
'id': 'Indonesia', | |
'ie': 'Ireland', | |
'il': 'Israel', | |
'it': 'Italy', | |
'jp': 'Japan', | |
'kw': 'Kuwait', | |
'lu': 'Luxembourg', | |
'my': 'Malaysia', | |
'mx': 'Mexico', | |
'ma': 'Morocco', | |
'nl': 'Netherlands', | |
'nz': 'New Zealand', | |
'ng': 'Nigeria', | |
'no': 'Norway', | |
'om': 'Oman', | |
'pk': 'Pakistan', | |
'pa': 'Panama', | |
'pe': 'Peru', | |
'ph': 'Philippines', | |
'pl': 'Poland', | |
'pt': 'Portugal', | |
'qa': 'Qatar', | |
'ro': 'Romania', | |
'ru': 'Russia', | |
'sa': 'Saudi Arabia', | |
'sg': 'Singapore', | |
'za': 'South Africa', | |
'kr': 'South Korea', | |
'es': 'Spain', | |
'se': 'Sweden', | |
'ch': 'Switzerland', | |
'tw': 'Taiwan', | |
'th': 'Thailand', | |
'tr': 'Turkey', | |
'ua': 'Ukraine', | |
'ae': 'United Arab Emirates', | |
'gb': 'United Kingdom', | |
'us': 'United States', | |
'uy': 'Uruguay', | |
've': 'Venezuela', | |
'vn': 'Vietnam', | |
// Aliases | |
'uk': 'United Kingdom', | |
}[countryCode]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment