2

I have some arbitrary number, which in my use-case is between 1,000 and 1,000,000. I need to format this value into a display value, in which it's being displayed as money. I.e 1000 -> "$1,000", 100000 ->"$100,000", etc.

The problem I'm having is that my expression size is too large, and the AMP expressions are too limiting, so it's not easy to format numbers the way I wanted to. I can't use ".replace()", or any regex, and even using basic conditionals seems overly difficult (doesn't seem like I can even use a standard ternary operator: a?b:c;).

Here's what I have

  //tells me how many digits i have
  amp-bind-macro#getLog10(arguments="num" expression="num.length - 1")

  //determines if the number should have a comma after it
  amp-bind-macro#numShouldHaveComma(arguments="log10" expression="log10%3==0 && log10!=0")

  //function to be invoked by Array.map()
  amp-bind-macro#formatNumber_map(arguments="char, ind, num" expression="(numShouldHaveComma(getLog10(round(num).toString().substr(ind+1))) && char+',') || char")

  //main formatter function (1000 -> 1,000)
  amp-bind-macro#formatNumber(arguments="num" expression="round(num).toString().split('').map((char,ind)=>formatNumber_map(char,ind, num)).join('')" )

  //adds $ and calls the formatter
  amp-bind-macro#formatMoney(arguments="val" expression="'$'+formatNumber(val)")

I have a display element set to call formatMoney when a slider's value changes, e.g.

<input type='range' on="input-throttled:AMP.setState({state:{mySlider:{value:event.value}}})" /> 

and

<div id='display-money' [text]="formatMoney(state.mySlider.value)">$1,000</div>

This particular way of doing it leaves me with a stack size of 53, which is more than the allowed maximum of 50.

The reason I do round(num).toString() is that I seemed to be getting inconsistent types - sometimes it's a Number, and other times a String. This way, the type is always parsed correctly, and no errors are thrown.

Is there a simpler way to format a number to look like money (whole dollars, comma-delimited by thousands)? If not, what can I do to my existing code to make it work?

Thanks!

nickrenfo2
  • 85
  • 1
  • 10

2 Answers2

2

The solution I came up with is as follows.

//determines how many "0's" there are.
amp-bind-macro#getLog10(arguments="num" expression="num.length - 1")

//determines if the number should have a comma after it
amp-bind-macro#numShouldHaveComma(arguments="log10" expression="log10%3==0 && log10!=0")

//helper function for formatNumber
amp-bind-macro#formatNumber_map(arguments="char, ind, numStr" expression="(numShouldHaveComma(getLog10(numStr.substr(ind)))) ? char+',' : char")

//main number formatter
amp-bind-macro#formatNumber(arguments="num, numStr" expression="numStr.split('').map((char,ind)=>formatNumber_map(char,ind, numStr)).join('')" )

//adds "$" and calls formatNumber
amp-bind-macro#formatMoney(arguments="val" expression="'$'+formatNumber(round(val), round(val).toString())")

Essentially, I've simplified the expression by explicitly passing the number as both a Number and a String to the next function, so that I don't need to call round(num).toString() in every single macro, only the first.

nickrenfo2
  • 85
  • 1
  • 10
-1

For your integer case formatNumber, i suggest a more compact implementation, in one amp-bind-macro:

<amp-bind-macro id="formatNumber" arguments="value" expression="(value<0?'-':'')+(abs(value)||0).toFixed().split('').map((v,i,a)=>(i&&(a.length-i)%3==0?',':'')+v).join('')"></amp-bind-macro>

If the values ​​are only positive, (value<0?'-':'')+ at the beginning expression can be deleted.

Full variant with decimals i posted here.