Skip to the solution
MediumMachine Coding14 min read

Build a Calculator

55 views

The problem

Question presented to candidate: "Build a basic calculator like the one on a phone: digits, a decimal point, plus, minus, times, divide, equals, clear and backspace. It should respect operator precedence, so 12 + 3 ร— 4 is 24, and it must not use eval. Make the keyboard work too."

Requirements:

  • Buttons for 0 to 9, ., + โˆ’ ร— รท, =, AC (clear all) and โŒซ (backspace); the display shows the expression as it is typed.
  • = evaluates with normal precedence (ร— and รท before + and โˆ’), without eval or new Function.
  • Pressing an operator right after another replaces it; a number can have only one decimal point; leading zeros are dropped; a leading minus makes a negative number.
  • After =, an operator continues from the result and a digit starts a new calculation.
  • Division by zero shows an error; the next key starts over.
  • Results display cleanly: 0.1 + 0.2 shows 0.3.
  • Physical keyboard support: digits, operators, Enter, Backspace, Escape.

What a strong answer should cover:

  • Separating the pure logic (a key-press reducer, a tokenizer, a parser) from the UI.
  • Why eval is unsafe and how a small recursive-descent parser (or shunting-yard) handles precedence.
  • Floating-point representation (0.1 + 0.2 === 0.30000000000000004) and rounding for display, plus overflow and tiny numbers.
  • The input state machine: what each key does after a result, after an error, after an operator.
  • Keyboard handling with a window listener that is cleaned up, and preventDefault so Enter does not also click a focused button.
  • Accessibility: named operator buttons, a live display.

Clarifying questions expected:

  • "Immediate execution like a basic pocket calculator (12 + 3 ร— 4 = 60), or precedence like a phone (24)?"
  • "Do we need parentheses, percent or memory keys?"
  • "How many digits can the display show?"

Code / implementation expected: Yes. A working calculator with a separate, testable evaluator that does not use eval.

  • React
  • State
  • Logic

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

14 min read ยท 9 sections

React solution

Every file of the working solution. Open it in the playground to run and change it.

ReactComplete solution
import { useEffect, useReducer } from "react";
import { press, initialState, keyFromKeyboard, pretty } from "./src/utils/calc";

const KEYS = [
  { key: "AC", label: "AC", name: "All clear", kind: "fn" },
  { key: "โŒซ", label: "โŒซ", name: "Backspace", kind: "fn" },
  { key: "/", label: "รท", name: "Divide", kind: "op" },
  { key: "*", label: "ร—", name: "Multiply", kind: "op" },
  { key: "7" },
  { key: "8" },
  { key: "9" },
  { key: "-", label: "โˆ’", name: "Minus", kind: "op" },
  { key: "4" },
  { key: "5" },
  { key: "6" },
  { key: "+", label: "+", name: "Plus", kind: "op" },
  { key: "1" },
  { key: "2" },
  { key: "3" },
  { key: "=", label: "=", name: "Equals", kind: "eq" },
  { key: "0", wide: true },
  { key: ".", label: ".", name: "Decimal point" },
];

export default function App() {
  // press(state, key) is a pure reducer, so useReducer is a perfect fit.
  const [state, dispatch] = useReducer(press, initialState);

  useEffect(() => {
    function onKeyDown(e) {
      if (e.ctrlKey || e.metaKey || e.altKey) return;
      const key = keyFromKeyboard(e.key);
      if (!key) return;
      e.preventDefault(); // stops Enter from also clicking a focused button
      dispatch(key);
    }
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, []);

  const shown = state.error || pretty(state.expr) || "0";

  return (
    <main className="page">
      <section className="calc" aria-label="Calculator">
        <output
          className={"display" + (state.error ? " error" : "")}
          data-testid="display"
          aria-live="polite"
        >
          {shown}
        </output>
        <div className="keys">
          {KEYS.map((k) => (
            <button
              key={k.key}
              type="button"
              data-key={k.key}
              aria-label={k.name}
              className={["key", k.kind, k.wide ? "wide" : ""].join(" ")}
              onClick={() => dispatch(k.key)}
            >
              {k.label ?? k.key}
            </button>
          ))}
        </div>
      </section>
      <p className="hint">
        Keyboard works too: digits, + - * /, Enter, Backspace, Esc.
      </p>
    </main>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 76 of 107 in Machine coding

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextMedium

Build Autocomplete / Typeahead with Debounce & Highlight

Continue