import chunk from "lodash/chunk"; const words = { ones: [ "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", ], tens: [ "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety", ], teens: [ "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", ], }; export const numberToWords = (number: number = 0): string => { if (typeof number !== "number" || isNaN(number) || number === 0) { return "zero"; } else { const numberAsArray: string[] = number.toString().split(""); const groupsOfHundreds: string[][] = groupIntoHundreds(numberAsArray); if (groupsOfHundreds.length > 3) { return "number is too large"; } return convert(groupsOfHundreds); } } const sizes = (arrayLength: number): string[] => { switch (arrayLength) { case 1: return [""]; case 2: return ["thousand", ""]; case 3: return ["million", "thousand", ""]; default: return []; } }; const groupHasValue = (group: string[]): boolean => group.some((digit) => digit !== "0"); const convert = (groupsOfHundreds: string[][]): string => groupsOfHundreds .map((group, i) => { const isLast = groupsOfHundreds.length > 1 && groupsOfHundreds.length - 1 === i; return groupHasValue(group) ? `${convertHundreds(group, isLast)} ${ sizes(groupsOfHundreds.length)[i] }` : ""; }) .join(" ") .replace(/\s{2,}/g, " ") .trim(); const convertHundreds = (group: string[], isLast: boolean): string => { const tensInt: number = parseInt(group[1] + group[2]); const tensWord: string = convertTensAndOnes(tensInt); const hundredsInt: number = parseInt(group[0]); const hundredsWord: string = hundredsInt > 0 ? `${words.ones[hundredsInt]} hundred` : ""; const and: string = (hundredsInt || isLast) && tensInt ? "and" : ""; return `${hundredsWord} ${and} ${tensWord}`.trim(); }; const convertTensAndOnes = (tens: number): string => { if (tens < 10) { return words.ones[tens]; } else if (tens >= 10 && tens < 20) { return words.teens[tens - 10]; } else { const tensWord = words.tens[Math.floor(tens / 10)]; const onesWord = words.ones[tens % 10]; const hyphen = tensWord !== "" && onesWord !== ""; return `${tensWord}${hyphen ? "-" : ""}${onesWord}`; } }; const groupIntoHundreds = (numberArray: string[]): string[][] => { let one: string, tens: string, hundreds: string[]; switch (numberArray.length % 3) { case 1: [one, ...hundreds] = numberArray; return [["0", "0", one], ...chunk(hundreds, 3, false)]; case 2: [tens, one, ...hundreds] = numberArray; return [["0", tens, one], ...chunk(hundreds, 3, false)]; default: return chunk(numberArray, 3, false); } };