Limiting decimal input in Calculator

Still me, still struggling with this!
I managed to clean up my code a lot, and I think it looks way better. I thought about converting value to a string and then doing an indexOf(".") to search for the dot, and only allow it if the indexOf returned -1. It worked, but it limited the possibilities (for instance, you could do “0.2” but not “0.2+0.2”). So, any ideas?

const button1 = [{number: 'one', value: '1'},{number: 'two', value: '2'},{number: 'three', value: '3'},{number: 'divide', value: '/'}];

const button2 = [{number: 'four', value: '4'},{number: 'five', value: '5'},{number: 'six', value: '6'},{number: 'add', value: '+'}];

const button3 = [{number: 'seven', value: '7'},{number: 'eight', value: '8'},{number: 'nine', value: '9'},{number: 'subtract', value: '-'}];

const button4 = [{number: 'zero', value: '0'},{number: 'decimal', value: '.'},{number: 'equals', value: '='},{number: 'multiply', value: '*'}];


  class Calculator extends Component {
  constructor(props) {
    super(props);
    this.state = { value: 0};
    this.handleClick = this.handleClick.bind(this);
  }


 handleClick(evt) {
    const id = evt.target.id;
    const result = evt.target.value;

switch(id) {
    case 'clear':
        this.setState({ value: 0});
        break;
    case 'equals':
      this.setState(prevState => ({
        value: math.eval(this.state.value)
      }));
        break;

    default: this.setState(prevState => ({
        value: `${prevState.value}${result}`
          .replace(/([/+\-/*=])([/+\-*=])/g, "$2")
          .replace(/^0+(?=[1-9])/, "")
          .replace(/^0+(?=\.)/, "0")
          .replace(/^0+\B/, "")
      })); 
}

}

render() {
    return(
            <div id="container">
                <Display value={this.state.value} />
                <div>
  {button1.map(el => <Button onClick={this.handleClick} key={el.index} id={el.number} value={el.value} />)}

                </div>
                <div>
 {button2.map(el => <Button onClick={this.handleClick} key={el.index} id={el.number} value={el.value} />)}
                </div>
                <div>
 {button3.map(el => <Button onClick={this.handleClick} key={el.index} id={el.number} value={el.value} />)}
                </div>
                <div>
{button4.map(el => <Button onClick={this.handleClick} key={el.index} id={el.number} value={el.value} />)}
                </div>
                <div>
                <Button onClick={this.handleClick} id="clear" value={'clear'}  />
                </div>
            </div>
)

}

}

So do you display the entire equation? Or do you take the numeric portion, push that onto a stack when an operator is added, then push THAT onto a stack when numbers are started again?

I did it the second way, which meant indexOf('.') worked for me.

If you’re going to do it the first way, with a long string displayed, then I’d suggest simply, on keyup or whatever, that you copy the string out, ditch everything BEFORE the last operator, then run indexOf() on what remains. If that returns -1, then add the decimal. If not, don’t.

1 Like

Sorry, you sort of lost me lol. What do you mean by stack? As in another part of the state?

For my calculator, I created an array, and simply pushed every step of the (potentially complex) equation onto that array.

So when the user was entering numbers, I could check for the period.

Then, when the user added an operator (+-*/), I pushed the number onto the stack, cleared the display, and showed the operator. If they did another operator, I simply updated the display.

But if they started entering numbers again, I pushed the displayed operator onto the array, and cleared the display to start showing the next number.

Pressing equal “compressed” the array, in effect reducing it to a single value, which was displayed and the array was empty.

Stack would be the “Value” in my code, right?

It would serve a similar purpose, yes. The advantage of keeping it as a string is that you can eval() it, assuming its formatted properly.

There’s no one right way to do this, so if the string is working for you thus far, then keep going with it. If you do, use the following logic to limit decimal points in the last number:

  • Transfer the display data to a variable.
  • Splice the string, removing everything up to the last operand.
  • Use indexOf('.') on what remains. At this point, you’re only viewing the last number, but you haven’t removed anything from the full display string.
  • If indexOf() returns -1, add that decimal! If not, simply fall through and don’t add anything.

That approach will work fine. I was simply overcomplicating mine, I think. Curse of an engineer’s mind.

1 Like

Thank you. But how do you check when an operand is in place? With a Regex?

Here’s an easy way to do just that. Wrapped in spoilers, for those who want to work out a solution on their own…

// Simulate value, and all the possible operators
let value = '12.224+36*8.025-9', 
    match = '[+-*/]';
// Make a temp string by getting the last index of an operator, and slicing.
let newString = value.slice(value.lastIndexOf(match)+value.length);

// Let's see what we got.
console.log(newString);

if (newString.indexOf('.') == -1) {
  // add that period!
} else {
  // Don't add the period, it's already there.
}
1 Like

Thank you. It´s like, I get what you are doing, I understand it but for some reason I have a hard time coming up with this on my own. Guess that´s where the experience kicks in.

It really does help to have a sounding board team. I have been at this for a while, but I still really find it useful to have people to bounce ideas off. Get weird looks sometimes. Still refining the team, mom doesn’t really understand about things like “Modified preorder tree traversal.”

Sorry, mom…

1 Like

Another way of doing the same thing (I’m a firm believer in overkill):

let value = '12.224+36*8.025-9', 
    match = '[+-*/]';
// We'll split the string at each instance of an operand, then we can
//   simply use pop() to get the LAST bit 
let newString = value.split(match).pop();
console.log(newString);

.[quote=“snowmonkey, post:10, topic:238040”]
It really does help to have a sounding board team. I have been at this for a while, but I still really find it useful to have people to bounce ideas off. Get weird looks sometimes. Still refining the team, mom doesn’t really understand about things like “Modified preorder tree traversal.”

Sorry, mom…
[/quote]
Hahaha. Hopefully I´ll get a job doing this next year and I´ll build up on my skills. I´m guessing being involved in an actual work environment really helps you grow. Does the code look good in your opinion?

Yes, I really think it does. Your components are clean, and you’re getting yourself comfortable with React. I think its a great start.

Also, if you’re interested in having others to bounce ideas or questions off, visit the Slack. Here’s a link to a conversation about it: Javascript learning buddy

1 Like

Will check it out, thanks!