pragi-string
Version:
A package to modify strings
156 lines (134 loc) • 6.37 kB
JavaScript
class PragiString {
/*** TEXT FORMATTERS ***/
static smartTruncate(text, maxLength, suffix = "...") {
if (text.length <= maxLength) return text;
const truncated = text.slice(0, maxLength).trim();
const lastSpace = truncated.lastIndexOf(" ");
return lastSpace > -1 ? truncated.slice(0, lastSpace) + suffix : truncated + suffix;
}
static formatText(text, transformFn, options = {}) {
if (typeof text !== "string") throw new Error("Input must be a string");
if (options.trim) text = text.trim();
if (!text) return text;
if (options.truncate) text = this.smartTruncate(text, options.truncate, options.suffix);
return transformFn(text);
}
static titleCase(text, options = {}) {
return this.formatText(
text,
txt => txt.toLowerCase().split(" ").map(word =>
options.excludeWords?.includes(word) ? word : word.charAt(0).toUpperCase() + word.slice(1)
).join(" "),
options
);
}
/*** NUMBER ↔ WORDS CONVERSION ***/
static toWords(num) {
if (num === 0) return "zero";
const belowTwenty = [
"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
"seventeen", "eighteen", "nineteen"
];
const tens = [
"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
];
const thousands = ["", "thousand", "million", "billion"];
function convertToWords(n) {
if (n < 20) return belowTwenty[n];
if (n < 100) return tens[Math.floor(n / 10)] + (n % 10 ? " " + belowTwenty[n % 10] : "");
if (n < 1000) return belowTwenty[Math.floor(n / 100)] + " hundred" + (n % 100 ? " " + convertToWords(n % 100) : "");
for (let i = 0; i < thousands.length; i++) {
let unit = 1000 ** (i + 1);
if (n < unit) {
return convertToWords(Math.floor(n / (unit / 1000))) + " " + thousands[i] +
(n % (unit / 1000) ? " " + convertToWords(n % (unit / 1000)) : "");
}
}
}
return convertToWords(num).replace(/\s+/g, ' ').trim();
}
static toNumbers(text) {
const numberMap = {
"zero": 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5, "six": 6, "seven": 7, "eight": 8, "nine": 9,
"ten": 10, "eleven": 11, "twelve": 12, "thirteen": 13, "fourteen": 14, "fifteen": 15, "sixteen": 16,
"seventeen": 17, "eighteen": 18, "nineteen": 19, "twenty": 20, "thirty": 30, "forty": 40, "fifty": 50,
"sixty": 60, "seventy": 70, "eighty": 80, "ninety": 90, "hundred": 100, "thousand": 1000, "million": 1000000
};
let wordsArray = words.toLowerCase().replace(/[-,]/g, "").split(/\s+/);
let result = 0, currentNumber = 0;
for (let word of wordsArray) {
if (numberMap[word] !== undefined) {
let value = numberMap[word];
if (value >= 100) {
currentNumber *= value;
} else {
currentNumber += value;
}
} else if (word === "and") {
continue; // Ignore "and"
} else {
throw new Error(`Unknown word in number: ${word}`);
}
if (currentNumber >= 1000) {
result += currentNumber;
currentNumber = 0;
}
}
return result + currentNumber;
}
/*** ORDINAL NUMBERS ***/
static toOrdinal(num) {
if (typeof num !== "number" || num < 1 || !Number.isInteger(num)) {
throw new Error("Input must be a positive integer.");
}
const suffixes = ["th", "st", "nd", "rd"];
const mod100 = num % 100;
const mod10 = num % 10;
return num + (suffixes[(mod100 - 20) % 10] || suffixes[mod100] || suffixes[0]);
}
/*** ROMAN NUMERAL CONVERSION ***/
static toRoman(num) {
if (typeof num !== "number" || num < 1) throw new Error("Invalid number");
const romanMap = [
[1000, "M"], [900, "CM"], [500, "D"], [400, "CD"],
[100, "C"], [90, "XC"], [50, "L"], [40, "XL"],
[10, "X"], [9, "IX"], [5, "V"], [4, "IV"], [1, "I"]
];
return romanMap.reduce((acc, [val, sym]) => {
while (num >= val) { acc += sym; num -= val; }
return acc;
}, "");
}
static fromRoman(roman) {
const romanToNum = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1};
let num = 0, i = 0;
while (i < roman.length) {
const twoChar = romanToNum[roman[i] + (roman[i + 1] || "")];
if (twoChar) { num += twoChar; i += 2; } else { num += romanToNum[roman[i]]; i++; }
}
return num;
}
/*** HUMAN-READABLE NUMBERS ***/
static humanizeNumber(num) {
if (typeof num !== "number") throw new Error("Input must be a number");
const suffixes = ["", "K", "M", "B", "T"];
let i = 0;
while (num >= 1000 && i < suffixes.length - 1) { num /= 1000; i++; }
return `${num.toFixed(1)}${suffixes[i]}`;
}
/*** TIME & DURATION FORMATTING ***/
static humanizeDuration(seconds) {
if (typeof seconds !== "number" || seconds < 0) throw new Error("Invalid duration");
const h = Math.floor(seconds / 3600), m = Math.floor((seconds % 3600) / 60), s = seconds % 60;
return `${h ? h + " hour, " : ""}${m ? m + " minute, " : ""}${s} seconds`.replace(/, $/, "");
}
static toDigitalTime(seconds) {
if (typeof seconds !== "number" || seconds < 0) throw new Error("Invalid duration");
const h = String(Math.floor(seconds / 3600)).padStart(2, "0");
const m = String(Math.floor((seconds % 3600) / 60)).padStart(2, "0");
const s = String(seconds % 60).padStart(2, "0");
return `${h}:${m}:${s}`;
}
}
module.exports = PragiString;