Count Without Sorting

You need the most common element in [3, 1, 4, 1, 5, 9, 1, 4]. The obvious move — sort the array, walk it, count consecutive runs — costs O(n log n). But the question you actually asked was about counting, not about ordering. Sorting drags every value past every other value just to learn how often each one appeared. Without a hash map, the only way to know how often a value repeats is to compare it to every other value. Eight elements is fine. A million is not. Walk through the array below — and watch the cost of looking up “have I seen this before?” before the map exists.

FIG. 1 — BUILD A FREQUENCY MAP
STEP 0 OF 8
3
0
1
1
4
2
1
3
5
4
9
5
1
6
4
7
— One pass. Eight elements. Every count known. —

One pass. Eight elements processed in eight steps. Every value's frequency known. The map paid for itself the moment the second 1 arrived — instead of rescanning everything to the left, the map jumped directly to the existing entry and added one. That O(1) lookup is what hash maps trade memory for: a fixed extra cost per unique value in exchange for a dramatic reduction in time. And unlike sorting, the original array order is untouched — useful in problems like “first element to appear twice” or “indices of all values with count above k”, where ordering would be lost.

Predict the Count

You built the map. Now read it under pressure. A frequency map is only useful if questions about it cost O(1) — otherwise you might as well rescan the array. Try the questions below. Shuffle the array and watch the map redraw — every prediction lands on a fresh count, so the answer cannot be memorised.

FIG. 2 — PREDICT THE COUNT
ARRAY · LENGTH 6
7
0
2
1
7
2
3
3
2
4
7
5
ADD VALUE
FREQUENCY MAP
KeyCount
73
22
31
QUESTION 1 OF 3

Looking at the live frequency map: which value has the **highest** count?

— Manipulate the array. Watch the map redraw live. Then answer. —

Three different uses of the same map: direct lookup (what is the count of one key — O(1)), local update (what changes when one element is added — only the affected key), and aggregate scan (find every entry matching a condition — O(k) where k is unique keys, not n). The gap between O(1) per question and O(n) per rescan is the entire reason the map exists. Build once. Query forever.

Group by Signature

Counting was the first trick. The second is grouping. Words like eat and tea and ate all sort to the same string aet — they share a signature. Without hashing, the only way to find which words belong together is to compare every pair: O(n²). Try grouping these six words yourself, and see how many comparisons you avoid by computing a key for each one independently and letting the map do the matching.

FIG. 3 — GROUP BY SIGNATURE
WORD 1 OF 6
“eat”
SORTED“aet”
— Sort each word's letters. Same sorted string, same bucket. —

Compute a signature, use it as a key, let the map do the grouping. The same trick works for anything that has a deterministic equivalence — transactions by date, files by extension, students by grade level, requests by status code. The only requirement is that the signature function is consistent: items that should land in the same group produce the same key, items that should not produce different keys. Sorted characters satisfy that for anagrams. Once you internalise the pattern, you start seeing it everywhere.

Write the Pattern

You have seen three faces of the frequency pattern: counting occurrences, querying pre-computed counts, and grouping by signature. They share the same core loop — a single pass that builds a map. Time to write that loop yourself.

The whole pattern hinges on one subtle line: freq.get(val) ?? 0. When you ask the map for a key it has never seen, get returns undefined — not 0. The nullish coalescing operator ?? catches that undefined and substitutes 0, so the first occurrence of any value starts at count 1. Without the ?? 0, the next line would compute undefined + 1, which produces NaN — and once NaN enters your map, every subsequent increment of that key stays NaN forever. The fallback is not a convenience; it is a correctness requirement.

Two blanks stand between you and a complete frequency counter. The first tests whether you understand the ?? 0 fallback. The second tests whether you understand the increment.

FIG. 4 — WRITE THE LOOP
1
const freq = new Map<number, number>();
2
for (const val of arr) {
3
const count = ___get-count___;
4
freq.set(val, ___new-count___);
5
}
— Two blanks. One pattern. The whole frequency-counting machine. —

This three-line loop — get with fallback, increment, set — is the skeleton behind dozens of LeetCode problems. Majority Element asks for the value with count above n / 2. Top K Frequent asks you to sort by count and take the top entries. Valid Anagram compares two frequency maps for equality. Group Anagrams uses the signature trick you just practised. Every time you need to count, group, or deduplicate, reach for a frequency map before reaching for .sort(). The pattern is always the same: one loop, one map, one ?? 0.