UNPKG

pragi-string

Version:

A package to modify strings

156 lines (134 loc) 6.37 kB
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;