Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get List of Supported Currencies

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 your JavaScript environment?

function getSupportedCurrencies() {
  function $(amount, currency) {
    let locale = 'en-US';
    let options = {
      style: 'currency',
      currency: currency,
      currencyDisplay: "name"
    };
    return Intl.NumberFormat(locale, options).format(amount);
  }
  const getAllPossibleThreeLetterWords = () => {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    const arr = [];
    let text = '';
    for (let i = 0; i < chars.length; i++) {
      for (let x = 0; x < chars.length; x++) {
        for (let j = 0; j < chars.length; j++) {
          text += chars[i];
          text += chars[x];
          text += chars[j];
          arr.push(text);
          text = '';
        }
      }
    }
    return arr;
  };
  let ary = getAllPossibleThreeLetterWords();
  let currencies = [];
  const rx = /(?<= ).+/; // This line doesn't work in Firefox versions older than version 78 due to bug 1225665: https://bugzilla.mozilla.org/show_bug.cgi?id=1225665
  ary.forEach((cur) => {
    let output = $(0, cur).trim();
    if (output.replace(/^[^ ]+ /, '') !== cur) {
      let obj = {};
      obj.code = cur;
      obj.name = output.match(rx)[0];
      currencies.push(obj);
    }
  });
  return currencies;
}
console.log(getSupportedCurrencies());
like image 280
Lonnie Best Avatar asked Oct 15 '22 11:10

Lonnie Best


2 Answers

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;
  }, [[]]);
}

Demo

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; }
like image 142
Mr. Polywhirl Avatar answered Nov 12 '22 13:11

Mr. Polywhirl


Right now, 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.

In the future, however, a spec proposal that exposes the set of recognized currencies is well on the way to standardization. Initial implementations will probably start showing up in web browsers' JS implementations before the end of 2021, letting you do this:

// This will return an array of currency codes supported
// by Intl.NumberFormat and Intl.DisplayNames, e.g.:
//   ["ADP", "AED", ..., "JPY", ..., "USD", ...]
var currencies = Intl.supportedValuesOf("currency");
like image 36
Jeff Walden Avatar answered Nov 12 '22 13:11

Jeff Walden