snippetjavascriptTip
Modeling money, currencies & exchange rates using JavaScript
Viewed 0 times
moneyjavascriptmodelingratesexchangecurrenciesusing
Problem
Working with money, currencies and exchange rates is pretty common, yet it's no easy task no matter the language. While JavaScript doesn't have a lot of useful features built into it,
The first step in modeling any sort of monetary value is to have a structure for currency. Luckily,
> [!NOTE]
>
> We've previously covered formatting a number into a currency string, which might cover some simpler use cases.
Intl can give us a head start with some parts of the process.The first step in modeling any sort of monetary value is to have a structure for currency. Luckily,
Intl has this problem solved for us. You can use Intl.NumberFormat with style: 'currency' to get a formatter for a specific currency. This formatter can then be used to format a number into a currency string.> [!NOTE]
>
> We've previously covered formatting a number into a currency string, which might cover some simpler use cases.
Solution
const isoCodes = Intl.supportedValuesOf('currency');
const currencyFields = ['symbol', 'narrowSymbol', 'name'];
const allCurrencies = new Map(
isoCodes.map(code => {
const format = currencyDisplay => value =>
Intl.NumberFormat(undefined, {
style: 'currency',
currency: code,
currencyDisplay,
})
.format(value)
.trim();
return [code, Object.freeze({ code, format })];
})
);
// Returns a Map object with all currency information
// {
// 'USD': { code: 'USD', format: [Function] },
// 'EUR': { code: 'EUR', format: [Function] },
// ...
// }> [!NOTE]
>
> We've previously covered formatting a number into a currency string, which might cover some simpler use cases.
In order to retrieve all supported currencies, we can use
Intl.supportedValuesOf() with 'currency' as the argument. This will return an array of the ISO 4217 currency codes supported by the environment. Then, using Map(), Array.prototype.map() and Intl.NumberFormat, we can create an object for all currencies, that will format values on demand.Notice how
Object.freeze() is used to prevent the object from being modified. This is important because we don't want to accidentally change the currency information.Having set up all the currency information, we need a way to retrieve it when we need it. Getting the same currency object for the same currency code will be important later down the line for comparisons. As we have a
Map object, we can use Map.prototype.get() to retrieve the currency object. As a safeguard, we should ensure the currency code matches the key, using String.prototype.toUpperCase().Code Snippets
const isoCodes = Intl.supportedValuesOf('currency');
const currencyFields = ['symbol', 'narrowSymbol', 'name'];
const allCurrencies = new Map(
isoCodes.map(code => {
const format = currencyDisplay => value =>
Intl.NumberFormat(undefined, {
style: 'currency',
currency: code,
currencyDisplay,
})
.format(value)
.trim();
return [code, Object.freeze({ code, format })];
})
);
// Returns a Map object with all currency information
// {
// 'USD': { code: 'USD', format: [Function] },
// 'EUR': { code: 'EUR', format: [Function] },
// ...
// }const getCurrencyFromCode = code => {
const isoCode = code.toUpperCase();
return allCurrencies.get(isoCode);
};
const currency = getCurrencyFromCode('usd');
currency.format('symbol')(1000); // '$1,000.00'class Currency {
static get(code) {
const currency = getCurrencyFromCode(code);
if (!currency)
throw new RangeError(`Invalid currency ISO code "${code}"`);
return currency;
}
static wrap(currency) {
if (
typeof currency === 'object' &&
getCurrencyFromCode(currency.code) === currency
)
return currency;
return Currency.get(currency);
}
}
const usd = Currency.get('usd');
usd.format('symbol')(1000); // '$1,000.00'
const usd2 = Currency.wrap(usd);
usd === usd2; // trueContext
From 30-seconds-of-code: modeling-money-currency-exchange-rates
Revisions (0)
No revisions yet.