Other than just guessing (like I\'ve done below), is there a more direct and efficient way of reflectively retrieving a list of all currencies supported by
You could load a known list via this XML:
https://www.currency-iso.org/dam/downloads/lists/list_one.xml
The list was found here: https://www.currency-iso.org/en/home/tables/table-a1.html
<ISO_4217 Pblshd="2018-08-29">
<CcyTbl>
<CcyNtry>
<CtryNm>
UNITED KINGDOM OF GREAT BRITAIN AND NORTHERN IRELAND (THE)
</CtryNm>
<CcyNm>Pound Sterling</CcyNm>
<Ccy>GBP</Ccy>
<CcyNbr>826</CcyNbr>
<CcyMnrUnts>2</CcyMnrUnts>
</CcyNtry>
<CcyNtry>
<CtryNm>UNITED STATES OF AMERICA (THE)</CtryNm>
<CcyNm>US Dollar</CcyNm>
<Ccy>USD</Ccy>
<CcyNbr>840</CcyNbr>
<CcyMnrUnts>2</CcyMnrUnts>
</CcyNtry>
</CcyTbl>
</ISO_4217>
var xmlString = getSampleCurrencyXml();
var xmlData = (new window.DOMParser()).parseFromString(xmlString, "text/xml");
var knownCodes = [].slice.call(xmlData.querySelectorAll('Ccy')).map(n => n.textContent)
// Fetch the XML instead?
fetch('https://www.currency-iso.org/dam/downloads/lists/list_one.xml', { cache: 'default' })
.then(response => response.text())
.then(xmlStr => (new window.DOMParser()).parseFromString(xmlStr, "text/xml"))
.then(data => knownCodes = data); // This may not work in the Stack Snippet
console.log(getSupportedCurrencies().map(c => c.code + '\t' + c.name).join('\n'));
function getSupportedCurrencies() {
function $(amount, currency) {
return Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
currencyDisplay: 'name'
}).format(amount);
}
return knownCodes.reduce((currencies, cur) => {
return (output => {
return output.replace(/^[^ ]+ /, '') !== cur ?
currencies.concat({
code: cur,
name: output.match(/(?<= ).+/)[0]
}) :
currencies;
})($(0, cur).trim());
}, []);
}
function getSampleCurrencyXml() {
return `
<ISO_4217 Pblshd="2018-08-29">
<CcyTbl>
<CcyNtry>
<CtryNm>
UNITED KINGDOM OF GREAT BRITAIN AND NORTHERN IRELAND (THE)
</CtryNm>
<CcyNm>Pound Sterling</CcyNm>
<Ccy>GBP</Ccy>
<CcyNbr>826</CcyNbr>
<CcyMnrUnts>2</CcyMnrUnts>
</CcyNtry>
<CcyNtry>
<CtryNm>UNITED STATES OF AMERICA (THE)</CtryNm>
<CcyNm>US Dollar</CcyNm>
<Ccy>USD</Ccy>
<CcyNbr>840</CcyNbr>
<CcyMnrUnts>2</CcyMnrUnts>
</CcyNtry>
</CcyTbl>
</ISO_4217>
`;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
If you want to generate the codes still, you can use a product iterable.
The following is based on Python's itertools.product function.
let ary = product('ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''), 3).map(a => a.join(''));
function product(iterables, repeat) {
var argv = Array.prototype.slice.call(arguments), argc = argv.length;
if (argc === 2 && !isNaN(argv[argc - 1])) {
var copies = [];
for (var i = 0; i < argv[argc - 1]; i++) { copies.push(argv[0].slice()); }
argv = copies;
}
return argv.reduce((accumulator, value) => {
var tmp = [];
accumulator.forEach(a0 => value.forEach(a1 => tmp.push(a0.concat(a1))));
return tmp;
}, [[]]);
}
console.log(getSupportedCurrencies().map(c => c.code + '\t' + c.name).join('\n'));
function getSupportedCurrencies() {
function $(amount, currency) {
return Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
currencyDisplay: 'name'
}).format(amount);
}
let ary = product('ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''), 3).map(a => a.join(''));
return ary.reduce((currencies, cur) => {
return (output => {
return output.replace(/^[^ ]+ /, '') !== cur
? currencies.concat({ code : cur, name : output.match(/(?<= ).+/)[0] })
: currencies;
})($(0, cur).trim());
}, []);
}
function product(iterables, repeat) {
var argv = Array.prototype.slice.call(arguments), argc = argv.length;
if (argc === 2 && !isNaN(argv[argc - 1])) {
var copies = [];
for (var i = 0; i < argv[argc - 1]; i++) { copies.push(argv[0].slice()); }
argv = copies;
}
return argv.reduce((accumulator, value) => {
var tmp = [];
accumulator.forEach(a0 => value.forEach(a1 => tmp.push(a0.concat(a1))));
return tmp;
}, [[]]);
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
Exhaustive testing, as the accepted answer offers, is probably the most reasonable practical tack here. Moreover, new currencies do not arise, and old currencies do not die, with particular frequency. The currencies supported by any implementation being kept up to date, are going to reflect reality almost always. So the try-and-see approach really isn't going to fail much.
But to elaborate further on this from the spec side, the spec really only cares about currencies being "well-formed": three ASCII letters. If the resulting code is a known currency, you will get congenial behavior. Otherwise, you get roughly graceful fallback to the code itself. So there's debatably not a need to expose the supported list: currency codes are at least a relatively understandable thing to many users, probably, as in most UI seeing something like "3 USD" or "5 CAD" where prices or costs are implicated is going to connote the user's currency generally.