Skip to content

Hash-map patterns: count, group, index

Pick the shape (count, group or index), choose the key, and use a Map unless the keys are plain strings.

Predict first

Before you read: predict

ExamHall counts answer events per attempt. One service sends attempt IDs as numbers, another as strings:

What is line?

Code for this question
const events = [{ attemptId: 205 }, { attemptId: '205' }, { attemptId: 31 }, { attemptId: 205 }, { attemptId: 'x9' }];const counts = {};for (const e of events) counts[e.attemptId] = (counts[e.attemptId] ?? 0) + 1;export const line = Object.entries(counts).map(([k, n]) => `${k}:${n}`).join(' ');
Pick one

The 30-second version

Many list problems are one pass with a hash map: count (key to number), group (key to list) or index (key to item or position). Objects turn keys into strings and list integer-like keys first; a Map keeps keys as they are.

Pick the shape (count, group or index), choose the key, and use a Map unless the keys are plain strings.

How it works

Step 1 of 10

One pass, three shapes. Count: key to number. Group: key to list. Index: key to an item or position. Each reads the input once.

Analogy: A mushroom club's sorting table after a foray (foraging)

After the morning walk, members tip their baskets onto one long table and the club sorts the finds.

At the sorting table In the code
one tally mark per find on the species sheet count: key to a number (step 2)
the card box filed by basket number, so "whose basket was this?" is one pull index: key to an item (step 4)
the old label printer writes every tray label as text, so basket 7 and basket "7" share a tray object keys are strings (step 7)
the shelf puts numbered trays first, lowest number first, then named trays in arrival order objects reorder integer-like keys (step 8)
the new label printer prints each label exactly as written on the basket, number or word a Map (step 9)

In real terms: a hash map turns "find everything with this key" from a walk along the list into one lookup, if the key says exactly what belongs together.

Breaks down when: a tray is a physical place, while a key is a value the code computes: two equal-looking keys can still differ (a number and a string in a Map).

Contrasting case: five finds on a plate. Sorting by eye is fine; the pattern earns its keep as the list grows.

Examples, run for real

4.1 ExamHall EXH-4250: answer sheets by filtering per attempt

ExamHall built each candidate's answer sheet by filtering the whole event list once per attempt:

const sheets = attemptIds.map((id) => events.filter((e) => e.attemptId === id));

At exam size, 50,000 events from 2,500 attempts, the job crawled (EXH-4250). MR !4550 replaced it with one pass that files each event under its attempt in a Map (step 3). Both were counted at two smaller sizes, 20 events per attempt:

4.1 EXH-4250 filter per attempt, 4,000 events, 200 attempts: 800,000 event reads
4.1 MR !4550 one pass with a Map, same input: 12,200 counted operations (4,000 event reads, 4,200 Map calls, 4,000 list writes); the same sheets
4.1 EXH-4250 filter per attempt, 8,000 events, 400 attempts: 3,200,000 event reads
4.1 MR !4550 one pass with a Map, same input: 24,400 counted operations (8,000 event reads, 8,400 Map calls, 8,000 list writes); the same sheets
4.1 doubling events and attempts: filter ×4.00 · one pass ×2.00; reads = events × attempts, so 50,000 × 2,500 = 125,000,000 (arithmetic, not run)

Filtering reads every event once per attempt, so doubling both quadruples the reads (dsa-big-o-js-costs step 4). The one pass reads each event once and makes one or two Map calls per event, so doubling the events doubles the count.

4.2 Ticketing: seat sections as object keys and as Map keys

A box office counts sales per section; the sections arrive as numbers and as strings:

4.2 sales, in order: 101, 'VIP', '101', 7
4.2 plain object: 3 keys · '7':1 '101':2 'VIP':1
4.2 Map:          4 keys · 101:1 'VIP':1 '101':1 7:1

The object merged 101 and '101' (step 7) and moved the integer-like keys to the front, smallest first (step 8); the Map kept four keys, each with the type it was set with (step 9).

Try it

Answer first, then check. Nothing is saved.

Question 1 of 8

A stadium groups ticket scans by gate:

The report built from it lists no gates at all.

Which one change makes it list every ticket under its gate?

Code for this question
export function ticketsByGate(scans) {  const groups = new Map();  for (const s of scans) {    const list = groups.get(s.gate) || [];    list.push(s.ticket);  }  return groups;}
Pick one

Question 2 of 8

ExamHall builds each attempt's final answer sheet in one pass: one Map per attempt, keyed by question, keeping the answer with the latest at (events can arrive out of order). The loop's five lines are shuffled (the app splits an order on >, so the comparison reads <):

In what order does the loop run them, top first?

Code for this question
export function answerSheets(events) {  const sheets = new Map();  for (const e of events) {    /* the five lines, in order */  }  return sheets;}

Your order

  1. 1empty
  2. 2empty
  3. 3empty
  4. 4empty
  5. 5empty

Tap the lines in the order you expect.

Question 3 of 8

A payments ledger looks for two refunds that add up to a disputed amount. The draft stores each amount before it looks for the partner:

What does findPair([25, 30, 25, 20], 50) return?

Code for this question
export function findPair(amounts, target) {  const seen = new Map();  for (let i = 0; i < amounts.length; i++) {    seen.set(amounts[i], i);    const j = seen.get(target - amounts[i]);    if (j !== undefined) return [j, i];  }  return null;}
Pick one

Question 4 of 8

A word game in a chat app groups the words players typed into anagram sets, keyed on an object that holds the sorted letters:

How many groups does groups hold?

Code for this question
const words = ['listen', 'enlist', 'google', 'silent', 'gogole'];const groups = {};for (const w of words) {  const key = { letters: [...w].sort().join('') };  if (!groups[key]) groups[key] = [];  groups[key].push(w);}export const count = Object.keys(groups).length;
Pick one

Question 5 of 8

the interviewer says: "Given an array of numbers and a target, return how many pairs of positions hold two values that sum to the target. Do it in one pass." You have 15 minutes and a plain editor, and ExamHall's score-pairing check is the use case.

Before you type: say your test list out loud.

Name at least five tests, each with its input and what it expects.

Question 6 of 8

the interviewer says: "Given an array of numbers and a target, return how many pairs of positions hold two values that sum to the target. Do it in one pass." You have 15 minutes and a plain editor, and ExamHall's score-pairing check is the use case.

Now write it, in 15 minutes, from a blank file that exports countPairs.

Question 7 of 8

A ledger counts today's payments per currency:

What happens when this module runs?

Code for this question
const payments = [{ currency: 'EUR' }, { currency: 'USD' }, { currency: 'EUR' }];const counts = {};for (const p of payments) counts[p.currency]++;export const line = Object.entries(counts).map(([k, n]) => `${k}:${n}`).join(' ');
Pick one

Question 8 of 8

A chat moderation tool lists, in one pass, the senders in a busy room who posted only once:

The room's senders, in posting order, are [42, 7, 42, 19, 42, 3].

How many senders are in the list postedOnce returns?

Code for this question
export function postedOnce(ids) {  const counts = new Map();  const once = [];  for (const id of ids) {    counts.set(id, (counts.get(id) ?? 0) + 1);    if (counts.get(id) === 1) once.push(id);  }  return once;}
Pick one

Go deeper

Pro tips
  1. Use a Map when keys are not plain strings, or when order and size matter. When: choosing the dictionary for a count, group or index (steps 7–9). Verify (RAN, tips.mjs): (The curriculum row's tip, [SELF].)
    tip 1 keys 12, '12', 7 · object: 2 keys (string, string) · Map: 3 keys (number, string, number)
  2. Say the shape out loud before you code: "this is a count", "a group" or "an index", then name the key. When: the first minute of a list question in an interview (steps 1, 6). Node 24 also ships Map.groupBy for the group shape. Verify (RAN, tips.mjs):
    tip 2 typeof Map.groupBy on Node 24.21.0: function · gates E: e1 e2 · W: w1
Source

Vendor pages change, so their claims are labelled as of 2026-10-15; every behaviour this lesson grades is executed on Node 24.

Checked against the tools and docs as of 2026-10-15.