Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

34 chapters
⌕
Beginner10›
B1Complexity analysisB2Arrays & stringsB3HashingB4Two pointersB5Sliding windowB6Binary searchB7Sorting algorithmsB8Stacks & queuesB9Linked listsB10Basic recursion
Intermediate12›
I1TreesI2Tree problems in depthI3Heaps & priority queuesI4Graphs: representationI5Graph problemsI6BacktrackingI7DP: 1DI8DP: 2DI9Greedy algorithmsI10IntervalsI11Bit manipulationI12Matrix problems
Advanced12›
A1Advanced DPA2Union-FindA3Advanced graph algorithmsA4Minimum Spanning TreeA5TriesA6Segment & Fenwick treesA7String algorithmsA8Monotonic stack & queueA9Design problemsA10Advanced backtrackingA11Topological patternsA12Interview strategy
/ search[ ] chaptert top

DSA in JS levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
B3

Hashing

The single most common way an O(n²) brute force becomes O(n).

What a hash map actually does

A hash function turns a key into a number, and that number picks a "bucket" in an underlying array. Look-up doesn't search — it computes the bucket from the key and jumps straight there. That's why Map/Set/object lookups are O(1) average case: the cost of hashing the key doesn't grow with how many other keys are already stored.

key: "cat" hash("cat") = 2 0 1 2 3 4 "cat" lands here — no scanning
The hash turns "which bucket" into arithmetic instead of a search.

Map/Set vs plain objects

Map / SetPlain object
Key typesanything (objects, NaN, etc.)strings/symbols only — numbers get coerced
Size.size, O(1)Object.keys(o).length, O(n)
Iteration orderinsertion order, guaranteedmostly insertion order, but integer-like keys sort first
Accidental prototype keysimpossible"toString" in {} is true

In interviews, default to Map/Set unless there's a specific reason not to — it sidesteps a whole category of "wait, why is this key already there" bugs.

The pattern: trade space for time

Almost every "hashing" interview question is the same trade: spend O(n) space to remember what you've already seen, so a second O(n) pass (or even the same pass) can answer "have I seen this before?" in O(1) instead of O(n).

// Two Sum — brute force: O(n²) time, O(1) space
function twoSumSlow(nums, target) {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] + nums[j] === target) return [i, j];
    }
  }
}

// Two Sum — hashed: O(n) time, O(n) space
function twoSumFast(nums, target) {
  const seen = new Map(); // value → index
  for (let i = 0; i < nums.length; i++) {
    const need = target - nums[i];
    if (seen.has(need)) return [seen.get(need), i];
    seen.set(nums[i], i);
  }
}

Watch the map build up, step by step

nums = [2, 7, 11, 15], target = 9 — trace every iteration:

inums[i]need = target − nums[i]seen.has(need)?action
027no — map is emptystore {2 → 0}
172yes — seen.get(2) = 0return [0, 1] ✓

Notice the map is only ever looked up for the value we still need, and only ever written for values we've already passed. That single pass does the work a nested loop would need two passes (and O(n²) time) to do.

Say it like this → "I'll trade O(n) space for a hash map so each lookup is O(1) instead of O(n) — that turns the O(n²) nested-loop version into a single O(n) pass."

Frequency counting — the other 80% of hashing questions

function frequency(arr) {
  const counts = new Map();
  for (const x of arr) {
    counts.set(x, (counts.get(x) || 0) + 1);
  }
  return counts;
}

// anagram check: same characters, same counts
function isAnagram(a, b) {
  if (a.length !== b.length) return false;
  const counts = new Map();
  for (const c of a) counts.set(c, (counts.get(c) || 0) + 1);
  for (const c of b) {
    if (!counts.get(c)) return false;
    counts.set(c, counts.get(c) - 1);
  }
  return true;
}

Grouping — building a Map of arrays

// group anagrams: same sorted letters → same bucket
function groupAnagrams(words) {
  const groups = new Map();
  for (const word of words) {
    const key = [...word].sort().join("");
    if (!groups.has(key)) groups.set(key, []);
    groups.get(key).push(word);
  }
  return [...groups.values()];
}
⚠ Hash collisions aren't your problem, but worst case is V8's hash maps handle collisions internally, so you never write collision-resolution code. But a hash map's O(1) is an average case — a pathological hash function can degrade to O(n) per operation. You'll never need to defend against this in an interview, but "average case, not worst case" is the correct answer if asked.

When hashing is the wrong tool

  • You need order — a hash map doesn't sort. If the question wants sorted output or range queries, you likely want a sorted structure (or a heap) instead.
  • You need the closest match, not an exact one — hashing only answers "is this exact key present." Nearest-value questions want binary search on a sorted structure.
  • Memory is the actual constraint — if the problem explicitly asks for O(1) space, a hash map is disqualified by definition.
Practice this layer

Opens in the editor — write it, run it, and check it against real tests.

Two Sum5 tests · beginnerTop K Frequent Elements5 tests · intermediateLongest Consecutive Sequence5 tests · advancedHappy Number5 tests · beginnerRansom Note5 tests · beginnerIntersection of Two Arrays5 tests · beginnerIntersection of Two Arrays II5 tests · intermediateFirst Unique Character in a String5 tests · beginnerWord Frequency Top-K5 tests · intermediateValid Anagram5 tests · beginnerGroup Anagrams5 tests · intermediateIsomorphic Strings5 tests · intermediateWord Pattern5 tests · intermediate
←previousArrays & strings↑ CovernextTwo pointers→