DEV Community

coyla
coyla

Posted on

1

The 1000th calculator React

I coded the 1000xxxxth calculator to test React.
Please check the code or the app and give me feedbacks, your way to do, everything.

code: https://github.com/blade-sensei/react-calculator/tree/master/src

app: https://calculator-1000.firebaseapp.com/

calculator service

import { operators } from '../constants';
class Calculator {

  calculationExpression(calculExpression) {
    let result = calculExpression.split('');
    while (!this.isLastResult(result)) {
      const terms = this.priorityOperationTerms(result)
      let resultCalcul = this.calculation(terms.operands, terms.operator);
      resultCalcul = resultCalcul.toString();
      const deltaDeleteIndex = (terms.limitIndexes.end - terms.limitIndexes.start) + 1;
      result.splice(terms.limitIndexes.start, deltaDeleteIndex, resultCalcul);
    }

    return Number(result[0]);
  }

  isLastResult(expression) {
    return expression.length === 1;
  }

  calculation(operands, operator) {
    const [firstOperand, secondOperand] = operands;
    if (operator === operators.MULTIPLICATION) {
      return this.multiplication(firstOperand, secondOperand);
    } else if (operator === operators.DIVISION) {
      return this.division(firstOperand, secondOperand);
    } else if (operator === operators.ADDITION) {
      return this.addition(firstOperand, secondOperand);
    } 
    return this.subtraction(firstOperand, secondOperand);
  }

  //return limitIndexes to know where to replace in 
  //current string expressionn, priority operation by futur priority result
  priorityOperationTerms(expression) {
    let firstOperatorFound = false;
    let indexPriorityOperator;
    let operands = [];

    for (let [index, term] of expression.entries()) {
      if (this.isOperator(term)) {
        if (this.isPriorityOperator(term)) {
          indexPriorityOperator = index;
          break;
        } if (!firstOperatorFound) {
          indexPriorityOperator = index;
          firstOperatorFound = true;
        }
      }
    }
    const [operandLeft, operandLeftIndex ] = this.getLeftOperand(expression, indexPriorityOperator);
    const [operandRight, operandRightIndex]  = this.getRightOperand(expression, indexPriorityOperator);
    operands = [operandLeft, operandRight];
    const terms = {
      operator: expression[indexPriorityOperator],
      operands,
      limitIndexes: {
        start: operandLeftIndex,
        end: operandRightIndex,
      }
    }
    return terms;

  }

  getRightOperand(expression, index) {
    let rightLimit = expression.length;
    let leftLimit = index + 1;
    for (let endIndex = leftLimit; endIndex < expression.length; endIndex++) {
      const term = expression[endIndex];
      if (this.isOperator(term)) {
        rightLimit  = endIndex;
        break;
      }
    }
    let number = expression.slice(leftLimit, rightLimit);
    number = number.join('');
    number = Number(number);
    return [number, rightLimit - 1]
  }

  getLeftOperand(expression, index) {
    let leftLimit = 0;
    for (let endIndex = index - 1; endIndex >= 0; endIndex--) {
      const term = expression[endIndex];
      if (this.isOperator(term)) {
        leftLimit  = endIndex + 1;
        break;
      }
    }
    let number = expression.slice(leftLimit, index);
    number = number.join('');
    number = Number(number);
    return [number, leftLimit]
  }

  isPriorityOperator(operator) {
    return (
      operator ===  operators.MULTIPLICATION ||
      operator === operators.DIVISION
    );
  }

  isOperator(term) {
    return Object.values(operators).includes(term)
  }

  addition(first, second) {
    return (first + second).toFixed(2);
  }

  subtraction(first, second) {
    return (first - second).toFixed(2);
  }

  multiplication(first, second) {
    return (first * second).toFixed(2);
  }

  division(first, second) {
    return (first / second).toFixed(2);
  }

}

export default Calculator;

It deals with string calcul expression (i wanted to try this way)

calculator component


import React from 'react';
import CalculatorButton from './CalculatorButton';
import CalculatorDisplayer from './CalculatorDisplayer';
import Calculator from '../services/calculator';
import './Calculator.css';
import { inputs, operators } from '../constants'

class CalculatorComponent extends React.Component {
  constructor(props) {
    super(props);
    this.calculator = new Calculator();
    this.state = {
      calculationExpression: '0',
    }
    this.handleUserInput = this.handleUserInput.bind(this);
  }


  handleUserInput(userInput) {
    const { type, value } = userInput;
    const lastUserInput = this.lastUserInput();

    switch(type) {
      case inputs.NUMBER:
        if (this.state.calculationExpression === '0') {
          return this.setState({
          calculationExpression: value,
        });
        }
        this.append(value);
        break;

      case inputs.OPERATOR: 
        if (lastUserInput.type === inputs.NUMBER) {
          this.append(value);
        }
        break;

      case inputs.SEPARATOR:
        if (lastUserInput.type === inputs.NUMBER) {
          const lastNumber = this.getLastNumberInExpression();
          if (!this.isFloat(lastNumber)) {
            this.append(value);
          }
        }
        break;

      case inputs.CLEAR:
        this.clearExpression(value);
        break;

      case inputs.RESULT: 
        if (this.lastUserInput().type === inputs.NUMBER ) {
          const normalizedExpression = this.normalizeToCalculation(this.state.calculationExpression);
          let result = this.calculator.calculationExpression(normalizedExpression);
          result = result.toString();
          result = this.normalizeToDisplay(result);
          this.setState({ calculationExpression: result });
        }
        break;

      default:
        break;
    }
  }

  append(input) {
    this.setState({
      calculationExpression: this.state.calculationExpression + input,
    });
  }

  isFloat(stringNumber) {
    return stringNumber.includes(',');
  }

  normalizeToCalculation(expression) {
    let expressionNormalized = expression;
    const regexMultiply = /x/gi;
    const regexSeparator = /,/gi;
    expressionNormalized = expressionNormalized.replace(regexMultiply, '*');
    expressionNormalized = expressionNormalized.replace(regexSeparator, '.');
    return expressionNormalized;
  }

  normalizeToDisplay(expression) {
    let expressionNormalized = expression;
    expressionNormalized = expressionNormalized.replace('.', ',');
    return expressionNormalized;
  }

  getLastNumberInExpression() {
    const expression = this.state.calculationExpression.split('');
    const rightLimit = expression.length;
    let leftLimit = 0;
    for (let termIndex = rightLimit; termIndex >= 0; termIndex--) {
      const term = expression[termIndex];
      if (this.isOperator(term)) {
        leftLimit  = termIndex + 1;
        break;
      }
    }
    const number = expression.slice(leftLimit, rightLimit + 1);
    return number.join('');
  }

  clearExpression(typeOfClear) {
    if (typeOfClear === inputs.CLEAR_ALL) {
      return this.resetExpression();
    } 
    return this.removeLastUserInput();
  }

  removeLastUserInput() {
    this.setState({
      calculationExpression: this.state.calculationExpression.slice(0, -1)
    })
  }

  resetExpression() {
    this.setState({ calculationExpression: '0'});
  }

  isOperator(term) {
    return Object.values(operators).includes(term)
  }

  lastUserInput() {
    const lastInput = this.state.calculationExpression.slice(-1);
    let type = '';
    if (!lastInput) return { type, lastInput } 
    if (!isNaN(Number(lastInput))) {
      type = inputs.NUMBER
    } else if (lastInput === ',') {
      type = inputs.SEPARATOR;
    } else {
      type = inputs.OPERATOR;
    } 
    return {
      type,
      lastInput,
    }
  }

  render() {
    return (
      <div className="calculator">
      <div className="displayer-container">
        <CalculatorDisplayer expression={this.state.calculationExpression} />  
      </div>
      <div className="inputs-container">
        <div className="row">
          <CalculatorButton value='AC' type='clear' onUserInput={this.handleUserInput} />
          <CalculatorButton value='' type='disabled' onUserInput={this.handleUserInput} />
          <CalculatorButton value='C' type='clear' onUserInput={this.handleUserInput} />
          <CalculatorButton value='/' type='operator' onUserInput={this.handleUserInput} />
        </div>

        <div className="row">
          <CalculatorButton value='7' type='number' onUserInput={this.handleUserInput} />
          <CalculatorButton value='8' type='number' onUserInput={this.handleUserInput} />
          <CalculatorButton value='9' type='number' onUserInput={this.handleUserInput} />
          <CalculatorButton value='x' type='operator' onUserInput={this.handleUserInput} />
        </div>

        <div className="row">
        <CalculatorButton value='4' type='number' onUserInput={this.handleUserInput} />
        <CalculatorButton value='5' type='number' onUserInput={this.handleUserInput} />
        <CalculatorButton value='6' type='number' onUserInput={this.handleUserInput} />
        <CalculatorButton value='-' type='operator' onUserInput={this.handleUserInput} />
        </div>

        <div className="row">
        <CalculatorButton value='1' type='number' onUserInput={this.handleUserInput} />
        <CalculatorButton value='2' type='number' onUserInput={this.handleUserInput} />
        <CalculatorButton value='3' type='number' onUserInput={this.handleUserInput} />
        <CalculatorButton value='+' type='operator' onUserInput={this.handleUserInput} /> 
        </div>

        <div className="row">
        <CalculatorButton value='0' type='number' onUserInput={this.handleUserInput} />
        <CalculatorButton value=',' type='separator' onUserInput={this.handleUserInput} />
        <CalculatorButton value='=' type='result' onUserInput={this.handleUserInput}/>
        </div>

      </div>
      </div>
    )
  }
}

export default CalculatorComponent;


Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay