mexico-state-lookup.js

'use strict'
/**
 * @author Ian Harisay
 */

/**
 * @typedef {Object} MexicoState
 * @property {String} name Name of the federative entity
 * @property {Array<String>} abbreviation Conventional abbreviations
 * @property {String} internal internal Nu Skin name
 * @property {String} iso2 2-letter ISO code
 * @property {String} iso3 3-letter ISO code
 * @example
 *
 * {
 *   name: 'Michoacán',
 *   abbreviation: ['Mich.'],
 *   internal: 'MIC',
 *   iso2: 'MI',
 *   iso3: 'MIC'
 * }
 */

const { isString } = require('@nuskin/uncle-buck')

// state data taken from https://en.wikipedia.org/wiki/Template:Mexico_State-Abbreviation_Codes
const states = [
    { name: 'Aguascalientes', abbreviation: ['Ags.'], iso2: 'AG', iso3: 'AGU', internal: 'AGU' },
    { name: 'Baja California', abbreviation: ['B.C.'], iso2: 'BC', iso3: 'BCN', internal: 'BCN' },
    { name: 'Baja California Sur', abbreviation: ['B.C.S.'], iso2: 'BS', iso3: 'BCS', internal: 'BCS' },
    { name: 'Campeche', abbreviation: ['Camp.'], iso2: 'CM', iso3: 'CAM', internal: 'CAM' },
    { name: 'Chiapas', abbreviation: ['Chis.'], iso2: 'CS', iso3: 'CHP', internal: 'CHI' }, // different
    { name: 'Chihuahua', abbreviation: ['Chih.'], iso2: 'CH', iso3: 'CHH', internal: 'CHH' },
    { name: 'Coahuila', abbreviation: ['Coah.'], iso2: 'CO', iso3: 'COA', internal: 'COA' },
    { name: 'Colima', abbreviation: ['Col.'], iso2: 'CL', iso3: 'COL', internal: 'COL' },
    { name: 'Mexico City', abbreviation: ['CDMX'], iso2: 'DF', iso3: 'CMX', internal: 'CMX' },
    { name: 'Durango', abbreviation: ['Dgo.'], iso2: 'DG', iso3: 'DUR', internal: 'DUR' },
    { name: 'Guanajuato', abbreviation: ['Gto.'], iso2: 'GT', iso3: 'GUA', internal: 'GUA' },
    { name: 'Guerrero', abbreviation: ['Gro.'], iso2: 'GR', iso3: 'GRO', internal: 'GUE' }, // different
    { name: 'Hidalgo', abbreviation: ['Hgo.'], iso2: 'HG', iso3: 'HID', internal: 'HID' },
    { name: 'Jalisco', abbreviation: ['Jal.'], iso2: 'JA', iso3: 'JAL', internal: 'JAL' },
    { name: 'México', abbreviation: ['Méx.'], iso2: 'EM', iso3: 'MEX', internal: 'MEX' },
    { name: 'Michoacán', abbreviation: ['Mich.'], iso2: 'MI', iso3: 'MIC', internal: 'MIC' },
    { name: 'Morelos', abbreviation: ['Mor.'], iso2: 'MO', iso3: 'MOR', internal: 'MOR' },
    { name: 'Nayarit', abbreviation: ['Nay.'], iso2: 'NA', iso3: 'NAY', internal: 'NAY' },
    { name: 'Nuevo León', abbreviation: ['N.L.'], iso2: 'NL', iso3: 'NLE', internal: 'NL' }, // different
    { name: 'Oaxaca', abbreviation: ['Oax.'], iso2: 'OA', iso3: 'OAX', internal: 'OAX' },
    { name: 'Puebla', abbreviation: ['Pue.'], iso2: 'PU', iso3: 'PUE', internal: 'PUE' },
    { name: 'Querétaro', abbreviation: ['Qro.'], iso2: 'QT', iso3: 'QUE', internal: 'QUE' },
    { name: 'Quintana Roo', abbreviation: ['Q. Roo.','Q.R.'], iso2: 'QR', iso3: 'ROO', internal: 'QR' }, // different
    { name: 'San Luis Potosí', abbreviation: ['S.L.P.'], iso2: 'SL', iso3: 'SLP', internal: 'SLP' },
    { name: 'Sinaloa', abbreviation: ['Sin.'], iso2: 'SI', iso3: 'SIN', internal: 'SIN' },
    { name: 'Sonora', abbreviation: ['Son.'], iso2: 'SO', iso3: 'SON', internal: 'SON' },
    { name: 'Tabasco', abbreviation: ['Tab.'], iso2: 'TB', iso3: 'TAB', internal: 'TAB' },
    { name: 'Tamaulipas', abbreviation: ['Tamps.'], iso2: 'TM', iso3: 'TAM', internal: 'TAM' },
    { name: 'Tlaxcala', abbreviation: ['Tlax.'], iso2: 'TL', iso3: 'TLA', internal: 'TLA' },
    { name: 'Veracruz', abbreviation: ['Ver.'], iso2: 'VE', iso3: 'VER', internal: 'VER' },
    { name: 'Yucatán', abbreviation: ['Yuc.'], iso2: 'YU', iso3: 'YUC', internal: 'YUC' },
    { name: 'Zacatecas', abbreviation: ['Zac.'], iso2: 'ZA', iso3: 'ZAC', internal: 'ZAC' }
]

/**
 * _removeDiacritics replaces accents/diacritics for the ASCII character
 * @ignore
 * @param {String} str string to replace accents
 * @returns {String} replacement string
 */
const _removeDiacritics = (str) => {
    return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
}

/**
 * @ignore
 * @param {String} str The string to strip characters and diacritics from
 * @returns {String} Stripped down string
 */
const _strip = (str) => {
    if(isString(str)){
        str = _removeDiacritics(str)
            .replace(/[.]/g, '')
            .replace(/\s{2,}/g, ' ')
            .replace(/(^\s+|\s+$)/g, '')
            .toLowerCase()
    }

    return str
}

/**
 * _search searches for the code states data based on the field provided
 * @ignore
 * @param {Srting} field field to search for the code in.  field can be:
 * name, abbreviation, iso
 * @param {String} code
 * @returns {MexicoState} Returns a `MexicoState` object if one is found.  Else
 * an empty `Object` is returned.
 */
const _search = (field, code) => {
    let rv = {}
    OUTER: for(let state of states){
        if(Array.isArray(state[field])){
            for(let ele of state[field]){
                if (_strip(ele) === _strip(code)) {
                    rv = state
                    break OUTER
                }
            }
        } else if (_strip(state[field]) === _strip(code)) {
            rv = state
            break
        }
    }

    return rv
}

/**
 * byAbbr will search Mexico states by the conventional abbreviation
 * @memberof Utility.MexicoStateLookup
 * @param {String} abbr The conventional abbreviation to search by.
 * @returns {MexicoState} Returns a `MexicoState` object if one is found.  Else
 * an empty `Object` is returned.
 * @example
 * const msc = require('@nuskin/mexico-state-lookup')
 *
 * // How to lookup 'Nuevo León' by abbreviation
 * const state = msc.byAbbr('N.L.')
 */
const byAbbr = (abbr) => {
    return _search('abbreviation', abbr)
}

/**
 * byInternal will search Mexico states by Nu Skin's internal name.
 * @param {String} internalName The internal name to search by.
 * @returns {MexicoState} Returns a `MexicoState` object if one is found.  Else
 * an empty `Object` is returned.
 * @example
 * const msc = require('@nuskin/mexico-state-lookup')
 *
 * // How to lookup 'Nuevo León' by iso code
 * const state = msc.byIso('NL') // or 'NLE'
 */
const byInternal = (internalName) => {
    return _search('internal', internalName)
}

/**
 * byIso will search Mexico states by ISO2 or ISO3 codes
 * @param {String} code The iso code to search by.
 * @returns {MexicoState} Returns a `MexicoState` object if one is found.  Else
 * an empty `Object` is returned.
 * @example
 * const msc = require('@nuskin/mexico-state-lookup')
 *
 * // How to lookup 'Nuevo León' by iso code
 * const state = msc.byIso('NL') // or 'NLE'
 */
const byIso = (code) => {
    let rv = {}
    if (code.length === 2) {
        rv = _search('iso2', code.toUpperCase())
    }

    if (code.length === 3) {
        rv = _search('iso3', code.toUpperCase())
    }

    return rv
}

/**
 * byName will search Mexico states by the state name
 * @param {String} name The federative name to search for.
 * @returns {MexicoState} Returns a `MexicoState` object if one is found.  Else
 * an empty `Object` is returned.
 * @example
 * const msc = require('@nuskin/mexico-state-lookup')
 *
 * // How to lookup 'Nuevo León' by name
 * const state = msc.byName('Nuevo León')
 */
const byName = (name) => {
    return _search('name', name)
}

/**
 * byAll searches byAbbr, byInternal, byIso, and byName for a match of the given string
 * @param {String} str The string to search by.
 * @returns {MexicoState} Returns a `MexicoState` object if one is found.  Else
 * an empty `Object` is returned.
 * @example
 * const msc = require('@nuskin/mexico-state-lookup')
 *
 * // How to find the state by any attribute
 * const state = msc.byAll('Nuevo León')
 */
const byAll = (str) => {
    let rv = {}
    if(Object.keys(byAbbr(str)).length){
        rv = byAbbr(str)
    } else if(Object.keys(byIso(str)).length){
        rv = byIso(str)
    } else if(Object.keys(byName(str)).length){
        rv = byName(str)
    } else if(Object.keys(byInternal(str)).length){
        rv = byInternal(str)
    }

    return rv
}

/**
 * getStates returns a list of all the Mexico states.
 * @returns {Array<MexicoState>} Returns a list of all the Mexico states
 * @example
 * const msc = require('@nuskin/mexico-state-lookup')
 *
 * const states = msc.getStates()
 * states.forEach(state => {
 *   console.log({state})
 * })
 */
const getStates = () => {
    return JSON.parse(JSON.stringify(states))
}

module.exports = {
    byAll,
    byAbbr,
    byInternal,
    byIso,
    byName,
    getStates
}