Last active
September 13, 2023 02:45
-
-
Save alamilladev/52c873f1d956afbc533e3e58256fd314 to your computer and use it in GitHub Desktop.
Typescript function to format numbers with currency, thousand separators, fixed and significant decimals.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const defaultCurrency = { | |
id: 'USD', | |
symbol: '$', | |
full: 'USD$', | |
}; | |
export const isNegative = (num: number): boolean => num < 0; | |
/** | |
* =========================================== | |
* |******** CURRENCY FORMAT METHODS ********| | |
* =========================================== | |
*/ | |
/** | |
* Converts a number in scientific notation to its non-scientific representation. | |
* @param numString - The number in scientific notation as a string. | |
* @returns The number in non-scientific notation as a string. | |
*/ | |
function convertToNonScientific(num: string | number): string { | |
if (typeof num === 'number') { | |
num = String(num); | |
} | |
// Check if the number string is in scientific notation | |
if (num.includes('e')) { | |
// Convert to non-exponential form | |
const arrNum: string[] = num.split('e-'); | |
const strDecimal: string = arrNum[0].replace('.', ''); | |
const numDecimalsToSubtract: number = arrNum[0].split('.')[0].length; | |
const numZeros = Number(arrNum[1]) - numDecimalsToSubtract; | |
return '0.' + '0'.repeat(numZeros) + strDecimal; | |
} | |
return String(num); | |
} | |
/** | |
* Formats a number with the specified decimal places, ensuring the significant digits are retained. | |
* @param number - The number to format. | |
* @param decimalPlaces - The number of decimal places to keep after rounding. | |
* @returns The formatted number as a string with the specified decimal places. | |
*/ | |
function formatWithSignificantDecimals( | |
number: number, | |
decimalPlaces: number | |
): string { | |
// Convert the number to a string | |
let strNum: string = String(number); | |
strNum = convertToNonScientific(strNum); | |
// Find the decimal part of the number | |
const numParts: string[] = strNum.split('.'); | |
const decimalPart: string | undefined = numParts[1]; | |
// Find the number of leading zeros in the decimal part | |
let leadingZeros: number = 0; | |
if (decimalPart) { | |
while (decimalPart[leadingZeros] === '0') { | |
leadingZeros++; | |
} | |
} | |
// Determine the total digits after the decimal point considering leading zeros | |
const totalDigits: number = leadingZeros + decimalPlaces; | |
// Round the number | |
const roundedNumber: number = | |
Math.round(parseFloat(strNum) * Math.pow(10, totalDigits)) / | |
Math.pow(10, totalDigits); | |
return convertToNonScientific(roundedNumber); | |
} | |
export type FormatToCurrencyInput = { | |
num?: string | number | null; | |
currency?: string | null; | |
decimalPlaces?: number | null; | |
significantDecimalPlaces?: number | null; | |
}; | |
/** | |
* Formats a number as a currency string with the specified decimal places and currency symbol. | |
* @returns The formatted currency string. | |
*/ | |
export function formatToCurrency(input: FormatToCurrencyInput): string { | |
let num: number | string = input.num || 0; | |
let currency: string | null | undefined = input.currency; | |
const decimalPlaces: number = input.decimalPlaces || 2; | |
const significantDecimalPlaces: number = input.significantDecimalPlaces || 8; | |
if (!currency && currency !== '') currency = defaultCurrency.symbol; | |
if (typeof num === 'string') num = Number(num); | |
const absNum: number = Math.abs(num); | |
const negativeSimbol: string = isNegative(num) ? '-' : ''; | |
// Format numbers with thousand separator and fixed decimals (2 default decimals) | |
if (absNum > 1) { | |
num = negativeSimbol + absNum.toFixed(decimalPlaces); | |
return ( | |
currency + | |
Number(num).toLocaleString('en-US', { | |
minimumFractionDigits: decimalPlaces, | |
}) | |
); | |
} | |
// Format numbers with significan decimals (8 default decimals) | |
const numWithSigDecimals: string = formatWithSignificantDecimals( | |
absNum, | |
significantDecimalPlaces | |
); | |
const numParts: string[] = numWithSigDecimals.split('.'); | |
const decimalPart: string | undefined = numParts[1]; | |
if (!decimalPart || decimalPart.length < 2) { | |
return currency + negativeSimbol + Number(numWithSigDecimals).toFixed(2); | |
} | |
return currency + negativeSimbol + numWithSigDecimals; | |
} | |
/** | |
* Extracts a raw number from a string, removing any non-digit and non-decimal point characters. | |
* | |
* @param strNum - The string containing the number. | |
* @returns The raw number extracted from the string. | |
*/ | |
export function getRawNumber(strNum: string): number { | |
if (strNum === null || strNum === undefined) return 0; | |
// Remove any characters from the string that are not digits or decimal points. | |
// This regex [^\d.] matches any character that is not a digit or a decimal point. | |
const rawNumber: string = strNum.replace(/[^\d.]/g, ''); | |
const floatNumber: number = parseFloat(rawNumber); | |
return isNaN(floatNumber) ? 0 : floatNumber; | |
} | |
/** | |
* Rounds a number up to the specified number of decimal places. | |
* | |
* @param num - The number to round. | |
* @param decimalPlaces - The number of decimal places to keep after rounding. | |
* @returns The rounded number up to the specified decimal places. | |
*/ | |
export function roundUp(num: number, decimalPlaces: number): number { | |
// Multiply the number by 10 raised to the power of the decimal places. | |
// This moves the decimal point to the right, converting the decimals into integer digits. | |
const scaledNumber = num * Math.pow(10, decimalPlaces); | |
// Use Math.ceil to round up to the nearest integer. | |
const roundedNumber = Math.ceil(scaledNumber); | |
// Finally, divide the rounded number by 10 raised to the power of the decimal places | |
// to restore the decimal point to its original position and get the rounded result. | |
return roundedNumber / Math.pow(10, decimalPlaces); | |
} | |
export function roundDown(num: number, decimalPlaces: number): number { | |
// Multiply the number by 10 raised to the power of the decimal places. | |
// This moves the decimal point to the right, converting the decimals into integer digits. | |
const scaledNumber = num * Math.pow(10, decimalPlaces); | |
// Use Math.floor to round down to the nearest integer. | |
const roundedNumber = Math.floor(scaledNumber); | |
// Finally, divide the rounded number by 10 raised to the power of the decimal places | |
// to restore the decimal point to its original position and get the rounded result. | |
return roundedNumber / Math.pow(10, decimalPlaces); | |
} | |
export function formatPercentageChange(value: number | string | null) { | |
return `(${formatToCurrency({ num: value, currency: '' })} %)`; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment