Skip to content

Instantly share code, notes, and snippets.

@lewxdev
Last active July 11, 2024 18:03
Show Gist options
  • Save lewxdev/9cdfe794d5dde3bab504b1bc394720ed to your computer and use it in GitHub Desktop.
Save lewxdev/9cdfe794d5dde3bab504b1bc394720ed to your computer and use it in GitHub Desktop.
a utility function `$p()` that creates regular expression patterns from shorthand
/**
* Produces an array of locale-respective formats
* @template {keyof Intl.DateTimeFormatOptions} K
* @template {(NonNullable<Intl.DateTimeFormatOptions[K]>)[]} [F=["long", "short"]]
* @param {K} key - a valid key on the `Intl.DateTimeFormatOptions` object
* @param {number} quantity - the number of units of the provided option
* @param {(index: number) => Date} callback - used for creating subsequent dates
* @param {F} formats - an optional list of formats to generate and key
* @returns {{ [format in F[number]]: string }[]}
*/
const $f = (key, quantity, callback, formats = ["long", "short"]) =>
[...Array(quantity)].map((_, i) =>
formats.reduce((result, format) => ({
[format]: callback(i).toLocaleString("en", { [key]: format }),
...result
}), {})
)
/** A reference to the locale-generated named months */
const $M = $f("month", 12, (i) => new Date(0, i))
/** A reference to the locale-generated named weekdays */
const $D = $f("weekday", 7, (i) => new Date(0, 0, i))
/**
* The shorthand string mapping to regular expression
* components/patterns (as strings) for use in the interface
*/
const $S = {
// Currency patterns
/** Comma-separated values (e.g. "1,000.00") */
C0_000_00: "(?<!\\.|,|\\d)(?:(?:(?:\\d{1,3},\\d{2})*|\\d{1,2})\\d\\.\\d{2})\\b",
/** Non comma-separated values (e.g. "1000.00") */
C0000_00: "\\b\\d+\\.\\d{2}\\b",
// Readable date pattern components
/** @see {@link https://en.wikipedia.org/wiki/Date_format_by_country#Table_coding} */
/** 4-digit year (e.g. "2020") */
YYYY: "\\b\\d{4}\\b",
/** 2-digit year (e.g. 2020 as "20") */
YY: "\\b\\d{2}\\b",
/** The fully named month (e.g. "November") */
MMMM: `\\b${$M.map(({ long }) => long).join("|")}\\b`,
/** 3-letter, abbreviated named month (e.g. January as "Jan") */
MMM: `\\b${$M.map(({ short }) => short).join("|")}\\b`,
/** 2-digit, zero-leading numeric month (e.g. April as "04") */
MM: "\\b(?:0[1-9])|(?:1[0-2])\\b",
/** Non zero-loading numeric month (e.g. May as "5") */
M: "\\b[1-9]|(?:1[0-2])\\b",
/** The fully named weekday (e.g. "Wednesday") */
DDDD: `\\b${$D.map(({ long }) => long).join("|")}\\b`,
/** 3-letter, abbreviated named weekday (e.g. Friday as "Fri") */
DDD: `\\b${$D.map(({ short }) => short).join("|")}\\b`,
/** 2-digit, zero-leading day (e.g. 09 February as "09") */
DD: "\\b(?:0[1-9])|(?:[1-2][0-9])|(?:3[0-1])\\b",
/** Non zero-leading day (e.g. 7 December as "7") */
D: "\\b[1-9]|(?:[1-2][0-9])|(?:3[0-1])\\b",
}
/** An expression that matches the keys for the interface shorthand patterns */
const $P = new RegExp(Object.keys($S).join("|"), "g")
/**
* Dynamically converts shorthanded patterns from an input string to
* regular expression
* @param {string} input - the provided shorthanded string to convert
* @param {string=} flags - optional RegExp flags to create the expression with
* @returns - a regular expression object with named groups
* corresponding to the matched shorthand
*
* @example <caption>Date pattern</caption>
* // Equivalent pattern:
* /(?<YYYY>\b\d{4}\b)-(?<MM>\b(?:0[1-9])|(?:1[0-2])\b)-(?<DD>\b(?:0[1-9])|(?:[1-2][0-9])|(?:3[0-1])\b)/
*
* "2020-01-01".match($p("YYYY-MM-DD"))
*
* @example <caption>Currency pattern</caption>
* // Equivalent pattern:
* /(?<C0_000_00>(?<!\.|,|\d)(?:(?:(?:\d{1,3},\d{2})*|\d{1,2})\d\.\d{2})\b)/
*
* "1,749.99".match($p("C0_000_00"))
*/
const $p = (input, flags) =>
new RegExp(input.replace($P, (match) => `(?<${match}>${$S[match]})`), flags)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment