0

This is my first time posting in SO, and I need help formatting numbers correctly in a calculator app that I've made using ReactJS.

Here is the link on StackBlitz.

Now, I want to achieve the formatting effect after numbers are pressed and shown in the display and arithmetic signs are added, especially when multiple arithmetics are used.

To illustrate my point, below is a sample of the current display:

123456 + 7890123 * 11111

And what I want to achieve is this:

123,456 + 7,890,123 * 11,111

I could only do this when displaying the result using the toLocaleString() function. Even then, if I pressed number/numbers and then clicking the result button twice, it will be crashed (as the display contains a comma, and the evaluation function will not process it properly).

Hopefully, someone can point me out in the right direction. Thanks.

Pawel Veselov
  • 3,996
  • 7
  • 44
  • 62
iamkyo
  • 71
  • 1
  • 2
  • 7
  • This might be helpful to you. https://blog.usejournal.com/everything-react-first-app-188b33a880ca – Yushan Jun 27 '21 at 12:03
  • These one helps you too. https://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript . At first i think that you can display comma formatted numbers to screen in another state and your evaluate process happens in different state. – Deniz Firat Jun 27 '21 at 12:13
  • Hi there Yushan, the blog link does not contain formatting solutions. – iamkyo Jun 27 '21 at 12:27
  • Deniz, yeap I thats the most optimal way of doing it. Will try it later. Thanks. – iamkyo Jun 27 '21 at 12:29

2 Answers2

0

Quick Fix

You can remove ',' before evaluating the result. Change line 65 of Input.js to

setDisplay(evaluate(display.replace(/,/g, '')).toLocaleString());

Better Solution

Keep separate variables for Internal logical state and External Display state, where the former is valid for code and the latter is its visual representation.

You can achieve this by useEffect like this

/* --- Display.js --- */
const Display = ({ display }) => {
  const [printValue, setPrintValue] = useState('')
  useEffect(() => {
    setPrintValue(`${display}`.replace(/[0-9]+/g, num => (+num).toLocaleString()))
  }, [display])
  return (
    <StyledDisplay>
      {' '}
      <span>{printValue}</span>{' '}
    </StyledDisplay>
  );
};

Also, in Input.js, update line 65 in handleResult to

setDisplay(evaluate(display));
Avinash Thakur
  • 1,640
  • 7
  • 16
0

For this kind of situations, I like to use regex. Here what you can do is to use a regex that matches 3 digits and add the comma as wanted. To simplify the regex I usually reverse the string:

const original = "123456 / 98765 * 22222"

function format(str) {
  const reversed = str.split('').reverse().join('')
  const formatted = reversed.replace(/(\d{3})(?=\d)/gm, `$1,`)
  return formatted.split('').reverse().join('')
}
console.log('original string : ', original)
console.log('result string : ',format(original))

You can use this function in your Display component, just before injecting the display prop like this

function format(str){
    const reversed = str.split('').reverse().join('')
    const formatted = reversed.replace(/(\d{3})(?=\d)/gm, `$1,`)
    return formatted.split('').reverse().join('')
}
const Display = ({ display }) => {
  return (
    <StyledDisplay>
      {' '}
      <span>{format(display)}</span>{' '}
    </StyledDisplay>
  );
};
Oriun
  • 245
  • 1
  • 7
  • Thanks, this works great too! Though I'm curious for the need to reverse the array, wouldn't work just fine if I don't use it? – iamkyo Jun 27 '21 at 13:14
  • If you don't reverse, the regex become more complex as you actually need to determine wether or not you need a separator. By reversing you just need to know how many digits are in a group `(\d{3}))`and if there are still at least one digits after the group `(?=\d)`. It should be possible to do it without reversing but I didn't search too much – Oriun Jun 27 '21 at 13:30
  • I see, that makes sense! Appreciate the explanation. – iamkyo Jun 27 '21 at 19:59