Last active
August 31, 2024 02:39
-
-
Save dinocarl/9e925d6b5d867a5e3851e0fefb4a1a9c to your computer and use it in GitHub Desktop.
Using Ramda to parse a template string, and apply data
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
const dot = (f, g) => (a) => f(g(a)); | |
const cmp = (list) => list.reduce(dot, (a) => a); | |
const pthOr = (fallback, keys, obj) => keys.reduce( | |
(acc, item) => typeof acc === 'undefined' || | |
acc === null || | |
acc === fallback || | |
typeof acc[item] === 'undefined' | |
? fallback | |
: acc[item], | |
obj | |
); | |
const pathOrKeysLast = (fallback) => (data) => (path) => pthOr(fallback, path, data); | |
const checkType = (obj) => Object.prototype.toString.call(obj).slice(8, -1); | |
const iz = (type) => (obj) => typeof obj !== 'undefined' && obj !== null && checkType(obj) === type; | |
const isFunction = is(Function); | |
const isNumber = is(Number); | |
const isntNil = complement(isNil); | |
const splitAtSpaces = (str) => str.split(' '); | |
const splitAtDots = (str) => str.split('.'); | |
const splitAtCRs = (str) => str.split('\n'); | |
const trimThenSplitAtSpaces = cmp([splitAtSpaces, trim]); | |
const dropDelimiters = ({ openDelimiter = '{', closeDelimiter = '}' }) => (str) => str.slice( | |
openDelimiter.length, | |
-closeDelimiter.length | |
); | |
const tokenPattern = ({ openDelimiter = '{', closeDelimiter = '}', interior = '' }) => new RegExp( | |
`${openDelimiter}${interior}${closeDelimiter}`, 'g' | |
); | |
const defPattern = ({ openDelimiter = '{', closeDelimiter = '}', interior = '' }) => new RegExp( | |
`${openDelimiter}def${interior}${closeDelimiter}`, 'g' | |
); | |
const decimalRound = curry((sigDigits, value) => Number( | |
Math.round( value + 'e' + sigDigits ) + 'e-' + sigDigits | |
)); | |
const percentage = curry((a, b) => compose( | |
multiply(100), | |
divide(a) | |
)(b)); | |
const isValidNumGT0 = num => (isntNil(num) | |
&& isNumber(num) | |
&& !identical(num, NaN) | |
&& gt(num, 0) | |
); | |
const chars = join(''); | |
const words = compose( | |
trim, | |
join(' ') | |
); | |
const commaSpaceSep = compose( | |
trim, | |
join(', '), | |
reject(isEmpty) | |
); | |
const commaSep = compose( | |
trim, | |
join(','), | |
reject(isEmpty) | |
); | |
const abbreviate = (str) => length(str) > 0 | |
? `${str[0]}.` | |
: ''; | |
const suffix = curry((suffix, str) => `${str}${suffix}`); | |
const relationPhrase = curry((a, b) => cond([ | |
[gt(a), always('greater than')], | |
[lt(a), always('lower than')], | |
[equals(a), always('the same as')], | |
[T, always('')] | |
])(b)); | |
const pct = (a, b) => { | |
const a1 = Number(a); | |
const b1 = Number(b); | |
return (isValidNumGT0(a1) && isValidNumGT0(b1)) | |
? compose( | |
suffix('%'), | |
toString, | |
decimalRound(3), | |
percentage(a1) | |
)(b1) | |
: ''; | |
}; | |
const pctFromDecimal = dec => { | |
const cleanedDec = Number(dec); | |
return (isValidNumGT0(cleanedDec)) | |
? compose( | |
suffix('%'), | |
toString, | |
decimalRound(2), | |
multiply(100) | |
)(cleanedDec) | |
: '' | |
}; | |
const greet = curry((fname, lname) => chars([ | |
commaSpaceSep([ | |
`Hey there`, | |
words([ | |
fname, | |
abbreviate(lname) | |
]) | |
]), | |
`!` | |
])); | |
const findInData = (data) => cmp([ | |
pathOrKeysLast('')(data), | |
splitAtDots | |
]); | |
const applyNReplace = ([hed, ...tll]) => iz('Function')(hed) | |
? hed(...tll) | |
: [].concat(hed, tll).join(' ') | |
; | |
const parseStr = config => data => cmp([ | |
trim, | |
applyNReplace, | |
map(findInData(data)), | |
splitAtSpaces, | |
dropDelimiters(config), | |
]); | |
const indicatorDelta = (indicator1, indicator2, timeframe) => { | |
return `${relationPhrase(indicator1, indicator2)} ${timeframe}`; | |
}; | |
const smplTmpltFns = { | |
'1week': 'a week ago', | |
'1month': 'a month ago', | |
'6month': '6 months ago', | |
'1year': 'a year ago', | |
'3year': '3 years ago', | |
pct, | |
pctFromDecimal, | |
greet, | |
indicatorDelta | |
}; | |
const smplTmplt = (config, template, data) => template | |
.trim() | |
.replace( | |
tokenPattern(config), | |
parseStr(config)(data) | |
); | |
const tokenConfig = { openDelimiter: '{', interior: '[a-zA-Z0-9\n _.-]*', closeDelimiter: '}', }; | |
const modelData = { | |
a: '1', | |
b: '21', | |
c: 2, | |
addresses: { | |
mailing: { | |
city: 'Chicago', | |
state: 'Il', | |
street: 'N Ashland Ave', | |
no: '2026' | |
}, | |
email: '[email protected]' | |
}, | |
names: { | |
first: 'Tilda', | |
last: 'Swinton' | |
}, | |
d: '.2313' | |
}; | |
const introTemplate = ` | |
{def | |
fname names.first | |
lname names.last | |
city addresses.mailing.city | |
x a | |
y b | |
} | |
{greet names.first names.last} Your rate in {addresses.mailing.city} is {pct a b}, {indicatorDelta c b 1year}, {pctFromDecimal d}`; | |
const template2 = JSON.stringify({ | |
toplevel: { | |
name: '/path/to/{a}/{b}/{addresses.mailing.city}/{missing}'} | |
}); | |
(smplTmplt(tokenConfig, introTemplate, Object.assign({}, smplTmpltFns, tokenConfig, modelData))); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment