UNPKG

mauss

Version:

lightweight, modular, type-safe utilities

143 lines (142 loc) 4.82 kB
/** Compares nullish values, sorting `null` and `undefined` to the end */ export function undefined(x, y) { if (x == null && y == null) return 0; return (x == null && 1) || (y == null && -1) || 0; } /** Compares boolean values, prioritizing `true` over `false` */ export function boolean(x, y) { return +y - +x; } /** * Put `(x, y)` for bigger number first, and `(y, x)` for smaller number first * @returns `y - x` which defaults to descending order */ export function number(x, y) { return y - x; } /** Compares bigint values, defaults to ascending order */ export function bigint(x, y) { return x < y ? -1 : x > y ? 1 : 0; } /** Compares symbols using its string values */ export function symbol(x, y) { return x.toString().localeCompare(y.toString()); } /** Compares string values using `.localeCompare` */ export function string(x, y) { for (const [pattern, exp] of Object.entries(patterns)) { const fn = { date, time }[pattern.split(':')[0]]; if (exp.test(x) && exp.test(y) && fn) return fn(x, y); } return x.localeCompare(y); } /** Compares generic object values using {@link inspect} */ export function object(x, y) { if (x === null) return 1; if (y === null) return -1; return inspect(x, y); } const primitives = { string, number, bigint, boolean, symbol, undefined }; /** Compares anything with anything */ export function wildcard(x, y) { if (x == null) return 1; if (y == null) return -1; const [xt, yt] = [typeof x, typeof y]; if (xt === 'function') return 0; if (xt !== yt || xt === 'object') { const xs = JSON.stringify(x); const ys = JSON.stringify(y); return string(xs, ys); } return primitives[xt](x, y); } /** * Recursively compares common object properties until the first difference is found * @returns `0` if both objects are identical or completely different, otherwise their respective primitive difference */ export function inspect(x, y) { const common = [...new Set([...Object.keys(x), ...Object.keys(y)])].filter((k) => k in x && k in y && typeof x[k] === typeof y[k] && x[k] !== y[k]); for (let i = 0, key = common[i], data = typeof x[key]; i < common.length && x[key] !== null && y[key] !== null; key = common[++i], data = typeof x[key]) { if (data === 'function') continue; if (data === 'object') return inspect(x[key], y[key]); const constrained = primitives[data]; if (data in primitives) return constrained(x[key], y[key]); } return 0; } // ---- patterned ---- const patterns = { 'date:complete': /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/, 'date:time': /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)/, date: /\d{4}-[01]\d-[0-3]\d/, time: /[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)/, }; /** Compares date or date-like string values */ export function date(x, y) { return new Date(y).getTime() - new Date(x).getTime(); } /** Compares time or time-like string values */ export function time(x, y) { return Date.parse(`2017/08/28 ${y}`) - Date.parse(`2017/08/28 ${x}`); } /** * A higher-order function that accepts a string as an identifier and an optional comparator function, it breaks up the identifier described by the dot (`.`) character and returns a curried function that accepts `(x, y)` with an object defined by the identifier. * * The optional comparator can be used when you have an existing custom sort function, e.g. in combination with `compare.order` to sort a set of string. * * @example * * ```javascript * import * as compare from 'mauss/compare'; * * const posts = [ * { date: { month: 'March' } }, * { date: { month: 'June' } }, * { date: { month: 'May' } }, * { date: { month: 'April' } }, * { date: { month: 'January' } }, * { date: { month: 'June' } }, * { date: { month: 'February' } }, * ]; * * const months = [ * 'January', * 'February', * 'March', * 'April', * 'May', * 'June', * 'July', * 'August', * 'September', * 'October', * 'November', * 'December', * ]; * * posts.sort(compare.key('date.month', compare.order(months))); * ``` */ export function key(identifier, comparator) { const trail = identifier.split('.'); const drill = (o) => trail.reduce((ret, prop) => ret[prop], o); return (x, y) => (comparator || wildcard)(drill(x), drill(y)); } /** * A higher-order function that accepts an array of strings and returns a comparator function that sorts the strings in the order they appear in the array. */ export function order(weights) { const m = {}; weights.forEach((v, i) => (m[v] = i)); return (x, y) => m[x] - m[y]; }