nhb-toolbox
Version:
A versatile collection of smart, efficient, and reusable utility functions and classes for everyday development needs.
106 lines (105 loc) • 3.64 kB
JavaScript
import { isNumber } from '../guards/primitives.js';
import { isNumericString } from '../guards/specials.js';
import { ORDINAL_UNDER_TEEN, THOUSANDS } from './constants.js';
import { _convertLessThanThousand } from './helpers.js';
/**
* * Converts a numeric value into its corresponding English word representation.
* @warning ***Supports numeric values up to `10e19` or `10^20` (one hundred quintillion).***
* @warning ***Decimal values are ignored; only the integer part is converted.***
* @param number - The number to convert into words.
* @returns The number converted in words.
*/
export function numberToWords(num) {
let number = Math.trunc(Number(num));
if (!Number.isFinite(number) || isNaN(number)) {
return 'Invalid Number!';
}
const isNegative = number < 0;
if (number === 0)
return 'zero';
number = Math.abs(number);
let i = 0;
let result = '';
while (number > 0) {
if (i >= THOUSANDS.length) {
return `Number exceeds supported range (max is 10e19 aka 10^20)`;
}
if (number % 1000 !== 0) {
const isLastGroup = i === 0 && number % 100 < 100;
const prefix = _convertLessThanThousand(number % 1000, isLastGroup);
result = `${prefix} ${THOUSANDS[i]} ${result}`;
}
number = Math.floor(number / 1000);
i++;
}
const finalResult = result.trim().replace(/\s+/g, ' ');
return isNegative ? `minus ${finalResult}` : finalResult;
}
/**
* * Converts a number to a Roman numeral.
* @param num - The number to convert. Number must be `between 1 and 3999`.
* @returns The Roman numeral representation.
*
* @example convertToRomanNumerals(29) → "XXIX"
*/
export const convertToRomanNumerals = (num) => {
let number = Number(num);
if (number <= 0 || number >= 4000)
throw new RangeError('Number must be between 1 and 3999');
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'],
];
let result = '';
for (const [value, numeral] of romanMap) {
while (number >= value) {
result += numeral;
number -= value;
}
}
return result;
};
/**
* * Converts a number, numeric string, or cardinal word string into its ordinal word representation.
*
* @param number - A number (e.g. `42`), numeric string (e.g. `"42"`), or cardinal word (e.g. `"forty-two"`).
* @returns The ordinal word form (always in lowercase) of the input.
*
* @example
* numberToWordsOrdinal(1); // "first"
* numberToWordsOrdinal("23"); // "twenty-third"
* numberToWordsOrdinal("twenty-three"); // "twenty-third"
*/
export function numberToWordsOrdinal(number) {
const TEEN_OR_HUNDRED = /(teen|hundred|thousand|(m|b|tr|quadr)illion)$/;
const UNDER_TEEN = /(zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)$/;
const _fixUnderTeen = (cardinal) => {
return ORDINAL_UNDER_TEEN[cardinal];
};
const wordNumber = isNumericString(number) || isNumber(number) ?
numberToWords(number)
: number?.trim()?.toLowerCase();
if (TEEN_OR_HUNDRED.test(wordNumber)) {
return wordNumber + 'th';
}
else if (/y$/.test(wordNumber)) {
return wordNumber.replace(/y$/, 'ieth');
}
else if (UNDER_TEEN.test(wordNumber)) {
return wordNumber.replace(UNDER_TEEN, _fixUnderTeen);
}
else {
return wordNumber;
}
}