Skip to content

Instantly share code, notes, and snippets.

@dinocarl
Last active August 31, 2024 02:39
Show Gist options
  • Save dinocarl/9e925d6b5d867a5e3851e0fefb4a1a9c to your computer and use it in GitHub Desktop.
Save dinocarl/9e925d6b5d867a5e3851e0fefb4a1a9c to your computer and use it in GitHub Desktop.
Using Ramda to parse a template string, and apply data
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