From Zero to AI

Lesson 1.6: Practice - Calculator

Duration: 60 minutes

Learning Objectives

By the end of this lesson, you will be able to:

  • Apply all JavaScript concepts learned in this module
  • Build a functional calculator step by step
  • Handle user input and edge cases
  • Practice combining variables, operators, conditions, and loops

Project Overview

We will build a command-line calculator that can:

  1. Perform basic math operations (+, -, *, /)
  2. Handle multiple calculations
  3. Validate user input
  4. Display formatted results

This project combines everything from Module 1:

  • Variables: Store numbers and results
  • Data Types: Work with numbers and strings
  • Operators: Perform calculations
  • Conditions: Choose operations and validate input
  • Loops: Allow multiple calculations

Step 1: Basic Calculation

Let's start with the simplest version - two numbers and an operation.

// Basic calculator - Step 1
const num1 = 10;
const num2 = 5;
const operation = '+';

let result;

if (operation === '+') {
  result = num1 + num2;
} else if (operation === '-') {
  result = num1 - num2;
} else if (operation === '*') {
  result = num1 * num2;
} else if (operation === '/') {
  result = num1 / num2;
} else {
  result = 'Invalid operation';
}

console.log(`${num1} ${operation} ${num2} = ${result}`);
// Output: 10 + 5 = 15

Try It Yourself

Change the values of num1, num2, and operation to test different calculations.


Step 2: Using Switch Statement

A switch statement is cleaner for choosing between operations.

// Calculator with switch - Step 2
const num1 = 20;
const num2 = 4;
const operation = '/';

let result;

switch (operation) {
  case '+':
    result = num1 + num2;
    break;
  case '-':
    result = num1 - num2;
    break;
  case '*':
    result = num1 * num2;
    break;
  case '/':
    result = num1 / num2;
    break;
  default:
    result = 'Unknown operation';
}

console.log(`${num1} ${operation} ${num2} = ${result}`);
// Output: 20 / 4 = 5

Step 3: Adding Input Validation

Real calculators need to handle edge cases.

// Calculator with validation - Step 3
const num1 = 10;
const num2 = 0;
const operation = '/';

// Validate inputs
if (typeof num1 !== 'number' || typeof num2 !== 'number') {
  console.log('Error: Both inputs must be numbers');
} else if (isNaN(num1) || isNaN(num2)) {
  console.log('Error: Invalid number');
} else {
  let result;
  let hasError = false;

  switch (operation) {
    case '+':
      result = num1 + num2;
      break;
    case '-':
      result = num1 - num2;
      break;
    case '*':
      result = num1 * num2;
      break;
    case '/':
      if (num2 === 0) {
        console.log('Error: Cannot divide by zero');
        hasError = true;
      } else {
        result = num1 / num2;
      }
      break;
    default:
      console.log(`Error: Unknown operation "${operation}"`);
      hasError = true;
  }

  if (!hasError) {
    console.log(`${num1} ${operation} ${num2} = ${result}`);
  }
}

Step 4: Creating a Calculate Function

Let's organize our code into a reusable function.

// Calculator function - Step 4
function calculate(num1, operation, num2) {
  // Validate numbers
  if (typeof num1 !== 'number' || typeof num2 !== 'number') {
    return 'Error: Both inputs must be numbers';
  }

  if (isNaN(num1) || isNaN(num2)) {
    return 'Error: Invalid number';
  }

  // Perform calculation
  let result;

  switch (operation) {
    case '+':
      result = num1 + num2;
      break;
    case '-':
      result = num1 - num2;
      break;
    case '*':
      result = num1 * num2;
      break;
    case '/':
      if (num2 === 0) {
        return 'Error: Cannot divide by zero';
      }
      result = num1 / num2;
      break;
    case '%':
      if (num2 === 0) {
        return 'Error: Cannot divide by zero';
      }
      result = num1 % num2;
      break;
    case '**':
      result = num1 ** num2;
      break;
    default:
      return `Error: Unknown operation "${operation}"`;
  }

  return `${num1} ${operation} ${num2} = ${result}`;
}

// Test the function
console.log(calculate(10, '+', 5)); // 10 + 5 = 15
console.log(calculate(10, '-', 3)); // 10 - 3 = 7
console.log(calculate(10, '*', 4)); // 10 * 4 = 40
console.log(calculate(10, '/', 2)); // 10 / 2 = 5
console.log(calculate(10, '/', 0)); // Error: Cannot divide by zero
console.log(calculate(2, '**', 8)); // 2 ** 8 = 256

Step 5: Multiple Calculations

Use an array to perform multiple calculations.

// Multiple calculations - Step 5
function calculate(num1, operation, num2) {
  if (typeof num1 !== 'number' || typeof num2 !== 'number') {
    return { error: 'Both inputs must be numbers' };
  }

  let result;

  switch (operation) {
    case '+':
      result = num1 + num2;
      break;
    case '-':
      result = num1 - num2;
      break;
    case '*':
      result = num1 * num2;
      break;
    case '/':
      if (num2 === 0) {
        return { error: 'Cannot divide by zero' };
      }
      result = num1 / num2;
      break;
    default:
      return { error: `Unknown operation: ${operation}` };
  }

  return { result, expression: `${num1} ${operation} ${num2} = ${result}` };
}

// Array of calculations to perform
const calculations = [
  { num1: 100, op: '+', num2: 50 },
  { num1: 100, op: '-', num2: 30 },
  { num1: 25, op: '*', num2: 4 },
  { num1: 144, op: '/', num2: 12 },
  { num1: 10, op: '/', num2: 0 },
];

console.log('Calculator Results:');
console.log('===================');

for (const calc of calculations) {
  const output = calculate(calc.num1, calc.op, calc.num2);

  if (output.error) {
    console.log(`${calc.num1} ${calc.op} ${calc.num2} = Error: ${output.error}`);
  } else {
    console.log(output.expression);
  }
}

Step 6: Calculator with History

Track all calculations for review.

// Calculator with history - Step 6
const history = [];

function calculate(num1, operation, num2) {
  let result;
  let error = null;

  switch (operation) {
    case '+':
      result = num1 + num2;
      break;
    case '-':
      result = num1 - num2;
      break;
    case '*':
      result = num1 * num2;
      break;
    case '/':
      if (num2 === 0) {
        error = 'Division by zero';
      } else {
        result = num1 / num2;
      }
      break;
    default:
      error = 'Unknown operation';
  }

  // Store in history
  const entry = {
    num1,
    operation,
    num2,
    result: error ? null : result,
    error,
    timestamp: new Date().toLocaleTimeString(),
  };
  history.push(entry);

  return entry;
}

function showHistory() {
  console.log('\n--- Calculation History ---');

  if (history.length === 0) {
    console.log('No calculations yet.');
    return;
  }

  for (let i = 0; i < history.length; i++) {
    const h = history[i];
    if (h.error) {
      console.log(`${i + 1}. ${h.num1} ${h.operation} ${h.num2} = ERROR (${h.error})`);
    } else {
      console.log(`${i + 1}. ${h.num1} ${h.operation} ${h.num2} = ${h.result}`);
    }
  }

  console.log('---------------------------\n');
}

function getLastResult() {
  if (history.length === 0) {
    return null;
  }
  return history[history.length - 1].result;
}

// Perform some calculations
calculate(50, '+', 25);
calculate(100, '-', 33);
calculate(12, '*', 12);
calculate(100, '/', 4);
calculate(10, '/', 0);

// Show all history
showHistory();

// Show statistics
const successfulCalcs = history.filter((h) => h.error === null);
console.log(`Total calculations: ${history.length}`);
console.log(`Successful: ${successfulCalcs.length}`);
console.log(`Errors: ${history.length - successfulCalcs.length}`);

Step 7: Complete Calculator

Here is the full calculator with all features combined.

// Complete Calculator - Final Version

// ============ Configuration ============
const OPERATIONS = {
  '+': { name: 'Addition', fn: (a, b) => a + b },
  '-': { name: 'Subtraction', fn: (a, b) => a - b },
  '*': { name: 'Multiplication', fn: (a, b) => a * b },
  '/': { name: 'Division', fn: (a, b) => a / b },
  '%': { name: 'Modulo', fn: (a, b) => a % b },
  '**': { name: 'Power', fn: (a, b) => a ** b },
};

// ============ State ============
const calculatorHistory = [];

// ============ Core Functions ============
function performCalculation(num1, operation, num2) {
  // Validate inputs
  if (typeof num1 !== 'number' || typeof num2 !== 'number') {
    return { success: false, error: 'Invalid input: numbers required' };
  }

  if (isNaN(num1) || isNaN(num2)) {
    return { success: false, error: 'Invalid input: NaN detected' };
  }

  // Check operation exists
  if (!OPERATIONS[operation]) {
    return { success: false, error: `Unknown operation: ${operation}` };
  }

  // Check division by zero
  if ((operation === '/' || operation === '%') && num2 === 0) {
    return { success: false, error: 'Cannot divide by zero' };
  }

  // Perform calculation
  const result = OPERATIONS[operation].fn(num1, num2);

  // Handle edge cases
  if (!isFinite(result)) {
    return { success: false, error: 'Result is not a finite number' };
  }

  // Record in history
  const entry = {
    id: calculatorHistory.length + 1,
    num1,
    operation,
    num2,
    result,
    timestamp: new Date().toISOString(),
  };
  calculatorHistory.push(entry);

  return {
    success: true,
    result,
    formatted: formatResult(num1, operation, num2, result),
  };
}

function formatResult(num1, operation, num2, result) {
  // Format numbers nicely
  const formatNum = (n) => {
    if (Number.isInteger(n)) {
      return n.toString();
    }
    return n.toFixed(4).replace(/\.?0+$/, '');
  };

  return `${formatNum(num1)} ${operation} ${formatNum(num2)} = ${formatNum(result)}`;
}

// ============ History Functions ============
function getHistory() {
  return [...calculatorHistory];
}

function clearHistory() {
  calculatorHistory.length = 0;
  console.log('History cleared.');
}

function printHistory() {
  console.log('\n========== Calculator History ==========');

  if (calculatorHistory.length === 0) {
    console.log('No calculations performed yet.');
  } else {
    for (const entry of calculatorHistory) {
      console.log(
        `#${entry.id}: ${formatResult(entry.num1, entry.operation, entry.num2, entry.result)}`
      );
    }
  }

  console.log('=========================================\n');
}

function getStatistics() {
  if (calculatorHistory.length === 0) {
    return null;
  }

  const results = calculatorHistory.map((h) => h.result);

  return {
    count: calculatorHistory.length,
    sum: results.reduce((a, b) => a + b, 0),
    average: results.reduce((a, b) => a + b, 0) / results.length,
    max: Math.max(...results),
    min: Math.min(...results),
  };
}

// ============ Demo ============
console.log('JavaScript Calculator Demo');
console.log('==========================\n');

// Basic operations
console.log('Basic Operations:');
console.log(performCalculation(10, '+', 5).formatted);
console.log(performCalculation(10, '-', 3).formatted);
console.log(performCalculation(10, '*', 4).formatted);
console.log(performCalculation(10, '/', 4).formatted);

// More operations
console.log('\nMore Operations:');
console.log(performCalculation(17, '%', 5).formatted);
console.log(performCalculation(2, '**', 10).formatted);

// Decimal numbers
console.log('\nDecimal Numbers:');
console.log(performCalculation(3.14159, '*', 2).formatted);
console.log(performCalculation(100, '/', 3).formatted);

// Error handling
console.log('\nError Handling:');
const divByZero = performCalculation(10, '/', 0);
console.log(`10 / 0 = ${divByZero.error}`);

const unknownOp = performCalculation(10, '^', 2);
console.log(`10 ^ 2 = ${unknownOp.error}`);

// Show history
printHistory();

// Show statistics
console.log('Statistics:');
const stats = getStatistics();
if (stats) {
  console.log(`- Calculations: ${stats.count}`);
  console.log(`- Sum of results: ${stats.sum.toFixed(2)}`);
  console.log(`- Average result: ${stats.average.toFixed(2)}`);
  console.log(`- Max result: ${stats.max}`);
  console.log(`- Min result: ${stats.min}`);
}

Running Your Calculator

Option 1: Node.js

Save the code to a file called calculator.js and run:

node calculator.js

Option 2: Browser Console

  1. Open your browser
  2. Press F12 to open Developer Tools
  3. Go to the Console tab
  4. Paste the code and press Enter

Exercises

Exercise 1: Add Square Root

Add a square root operation to the calculator. It only needs one number.

// Hint: Use Math.sqrt()
Solution
function squareRoot(num) {
  if (num < 0) {
    return { success: false, error: 'Cannot calculate square root of negative number' };
  }

  const result = Math.sqrt(num);
  return {
    success: true,
    result,
    formatted: `sqrt(${num}) = ${result}`,
  };
}

console.log(squareRoot(16).formatted); // sqrt(16) = 4
console.log(squareRoot(2).formatted); // sqrt(2) = 1.4142...
console.log(squareRoot(-4).error); // Cannot calculate...

Exercise 2: Chain Calculations

Use the result of one calculation as input for the next.

// Start with 10, add 5, multiply by 2, subtract 10
// Expected: ((10 + 5) * 2) - 10 = 20
Solution
let value = 10;

value = value + 5; // 15
console.log(`After +5: ${value}`);

value = value * 2; // 30
console.log(`After *2: ${value}`);

value = value - 10; // 20
console.log(`After -10: ${value}`);

console.log(`Final result: ${value}`);

// Or using our calculate function:
function chainCalculate(start, operations) {
  let result = start;

  for (const op of operations) {
    const calc = performCalculation(result, op.operation, op.value);
    if (!calc.success) {
      return { success: false, error: calc.error };
    }
    console.log(calc.formatted);
    result = calc.result;
  }

  return { success: true, result };
}

const chain = chainCalculate(10, [
  { operation: '+', value: 5 },
  { operation: '*', value: 2 },
  { operation: '-', value: 10 },
]);

console.log(`Chain result: ${chain.result}`); // 20

Exercise 3: Add Memory Feature

Add memory functions: store, recall, and clear.

// MC - Memory Clear
// MR - Memory Recall
// M+ - Add to Memory
Solution
let memory = 0;

function memoryClear() {
  memory = 0;
  console.log('Memory cleared');
}

function memoryRecall() {
  console.log(`Memory: ${memory}`);
  return memory;
}

function memoryAdd(value) {
  memory += value;
  console.log(`Added ${value} to memory. Memory: ${memory}`);
}

function memorySubtract(value) {
  memory -= value;
  console.log(`Subtracted ${value} from memory. Memory: ${memory}`);
}

// Demo
memoryClear(); // Memory cleared
memoryAdd(50); // Added 50. Memory: 50
memoryAdd(25); // Added 25. Memory: 75
memorySubtract(10); // Subtracted 10. Memory: 65
console.log(memoryRecall()); // Memory: 65
memoryClear(); // Memory cleared
console.log(memoryRecall()); // Memory: 0

Key Takeaways

  1. Break problems into steps - Start simple, add features gradually
  2. Validate inputs - Check for invalid data before processing
  3. Handle edge cases - Division by zero, invalid operations
  4. Keep history - Useful for debugging and user experience
  5. Format output - Make results readable
  6. Organize code - Use functions to separate concerns

What You Built

Congratulations! You have created a calculator that demonstrates:

  • Variable declaration and assignment
  • Working with numbers and strings
  • Arithmetic and comparison operators
  • Conditional logic with if/else and switch
  • Loops for processing multiple calculations
  • Arrays for storing history
  • Functions for code organization

Resources

Resource Type Description
MDN: Math Object Documentation Built-in math functions
JavaScript.info: Functions Tutorial Functions tutorial
Node.js Documentation Documentation Running JavaScript with Node

Module Complete!

You have finished Module 1: JavaScript Basics. You now understand:

  • How to store data in variables
  • Different data types and their uses
  • Operators for calculations and comparisons
  • Making decisions with conditions
  • Repeating code with loops

Next Step: Continue to Module 2 to learn why TypeScript improves on JavaScript and how to set it up.

Continue to Module 2: Introduction to TypeScript