Last active
June 20, 2022 20:04
-
-
Save steenhansen/5a0dbad5388a79ebb900b257fc7a129c to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/** | |
* ES6 versions of Either monad used in FP in JS | |
* Author: Luis Atencio | |
* https://github.com/luijar/functional-programming-js/blob/master/src/model/monad/Either.js | |
* Validation from from Quildreen Motta's | |
* https://folktale.origamitower.com/api/v2.3.0/en/folktale.validation.html | |
*/ | |
// Run the below examples in the console | |
const makeValidation = () => { | |
const the_failures = new Set(); | |
function failure(the_object, an_error) { | |
the_failures.add(an_error.message); | |
return new Right(the_object); | |
} | |
function success(the_object){ | |
return new Right(the_object); | |
} | |
class Either { | |
constructor(value) { | |
this._value = value; | |
} | |
getFailures() { | |
let the_errors = [...the_failures]; | |
the_failures.clear(); | |
return the_errors; | |
} | |
} | |
class Right extends Either { | |
map(f) { | |
return new Right(f(this._value)); | |
} | |
verifyProp(f) { | |
return f(this._value); | |
} | |
tap(f) { | |
f(this._value); | |
return new Right(this._value); | |
} | |
}; | |
return {failure, success}; | |
} | |
const validation = makeValidation(); | |
const BLANK_STR_OK = true; | |
const BLANK_STR_FAIL = false; | |
const STATES_REGEX = /^(AL|AK|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NC|ND|NE|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VT|WA|WI|WV|WY)$/ig; | |
const THE_STATES = {'alabama': 'AL', 'alaska': 'AK', 'arizona': 'AZ', 'arkansas': 'AR', 'california': 'CA', | |
'colorado': 'CO', 'connecticut': 'CT', 'delaware': 'DE', 'district of columbia': 'DC', | |
'florida': 'FL', 'georgia': 'GA', 'hawaii': 'HI', 'idaho': 'ID', 'illinois': 'IL', | |
'indiana': 'IN', 'iowa': 'IA', 'kansas': 'KS', 'kentucky': 'KY', 'louisiana': 'LA', | |
'maine': 'ME', 'maryland': 'MD', 'massachusetts': 'MA', 'michigan': 'MI', | |
'minnesota': 'MN', 'mississippi': 'MS', 'missouri': 'MO', 'montana': 'MT', | |
'nebraska': 'NE', 'nevada': 'NV', 'new hampshire': 'NH', 'new jersey': 'NJ', | |
'new mexico': 'NM', 'new york': 'NY', 'north carolina': 'NC', 'north dakota': 'ND', | |
'ohio': 'OH', 'oklahoma': 'OK', 'oregon': 'OR', 'pennsylvania': 'PA', | |
'rhode island': 'RI', 'south carolina': 'SC', 'south dakota': 'SD', 'tennessee': 'TN', | |
'texas': 'TX', 'utah': 'UT', 'vermont': 'VT', 'virginia': 'VA', 'washington': 'WA', | |
'west virginia': 'WV', 'wisconsin': 'WI', 'wyoming': 'WY' }; | |
const ZIP_REGEX = /^\d\d\d\d\d$/; | |
function decodeJson(some_json){ | |
try { | |
const json_obj = JSON.parse(some_json); | |
return validation.success(json_obj); | |
} catch (json_error){ | |
return validation.failure(json_error, json_error); | |
} | |
} | |
const checkState = key_name => address_object => { | |
const state_str = address_object[key_name]; | |
const state_type = typeof state_str; | |
if (state_type === 'string'){ | |
if (state_str.match(STATES_REGEX)){ | |
return validation.success(address_object); | |
} | |
} | |
return validation.failure(address_object, Error (`State not valid '${state_str}'`)); | |
} | |
const checkStr = (key_name, empty_str_ok) => address_object => { | |
const the_value = address_object[key_name]; | |
if (the_value === null) { | |
return validation.failure(address_object, Error (`Key '${key_name}' is null`)); | |
} | |
if (the_value === undefined) { | |
return validation.failure(address_object, Error (`Missing key of '${key_name}'`)); | |
} | |
const str_type = typeof the_value; | |
if (str_type !== 'string'){ | |
return validation.failure(address_object, Error (`Property '${key_name}' not a string, but '${str_type}'`)); | |
} | |
if (!empty_str_ok && the_value.length === 0){ | |
return validation.failure(address_object, Error (`Emtpy string for key '${key_name}'`)); | |
} | |
return validation.success(address_object); | |
} | |
const nonEmptyStr = key_name => checkStr(key_name, BLANK_STR_FAIL); | |
const justStr = key_name => checkStr(key_name, BLANK_STR_OK); | |
const checkZip = key_name => address_object => { | |
const zip_str = address_object[key_name]; | |
if (zip_str.match(ZIP_REGEX)){ | |
return validation.success(address_object); | |
} | |
return validation.failure(address_object, Error (`Zip not valid '${zip_str}'`)); | |
} | |
const toStrZip = key_name => address_object => { | |
const zip_val = address_object[key_name]; | |
let new_zip; | |
if (typeof zip_val === 'number'){ | |
const zip_str = zip_val.toString(); | |
new_zip = zip_str.padStart(5, '0'); | |
} else if (typeof zip_val === 'undefined'){ | |
new_zip = '_undef_'; | |
} else { | |
new_zip = zip_val.toString(); | |
} | |
const obj_zip_str = Object.assign({}, address_object, {zip:new_zip}); | |
return obj_zip_str; | |
} | |
const toShortState = key_name => address_object => { | |
const state_val = address_object[key_name]; | |
if (typeof state_val === 'string'){ | |
if (state_val in THE_STATES){ | |
const short_state = THE_STATES[state_val]; | |
const obj_2_state = Object.assign({}, address_object, {state:short_state}); | |
return obj_2_state; | |
} | |
} | |
return address_object; | |
} | |
const validateAddress = addr_json_str => { | |
const the_failures = decodeJson(addr_json_str) | |
.verifyProp(nonEmptyStr('first')) | |
.verifyProp(nonEmptyStr('last')) | |
.verifyProp(justStr('apt')) | |
.verifyProp(nonEmptyStr('street')) | |
.tap(cur_object=>console.log(' tap:', cur_object)) | |
.map(toShortState('state')) | |
.verifyProp(checkState('state')) | |
.map(toStrZip('zip')) | |
.verifyProp(checkZip('zip')) | |
.tap(cur_object=>console.log(' tap:', cur_object)) | |
.getFailures(); | |
return the_failures; | |
} | |
const showAddress = an_address => { | |
console.log('init:', an_address); | |
const address_errors = validateAddress(an_address); | |
console.log('errs:', address_errors); | |
console.log(); | |
} | |
var good_addr = '{"first" : "Rodney", "last":"Dangerfield", "apt":"", "street":"Battlecreek", "state": "district of columbia", "zip":123 }'; | |
var bad_first_state = '{"first" : "", "last":"Carlin", "apt":"", "street":"Battlecreek", "state": "New Utah", "zip":123 }'; | |
var bad_all = '{"" : "George", "surname":"Carlin", "apt":"", "street":"", "state": "", "zip":"abc" }'; | |
var bad_json = '_not_json_'; | |
var the_addresses = [good_addr, bad_first_state, bad_all, bad_json]; | |
var the_failures = the_addresses.map(showAddress); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment