Group Anagrams in JavaScript: Two Approaches (Sorted Key vs O(n·k) Count Key)
Quick answer
Group anagrams by bucketing words under a canonical key that all anagrams share. The simple key is each word's letters sorted alphabetically (O(n·k log k) for n words of length k). The optimal key avoids sorting: a 26-length character-count signature, which is O(n·k). Iterate once, build the key, push the word into a Map under that key, and return the map's values.
Short answer: All anagrams share a canonical form, so use it as a hash-map key. The simple key is the word's letters sorted (O(n·k log k)); the optimal key is a character-count signature that avoids sorting (O(n·k)). One pass, then return the map's values.
Problem. Given an array of strings, group the anagrams together.
Input: ["eat", "tea", "tan", "ate", "nat", "bat"]
Output: [["eat","tea","ate"], ["tan","nat"], ["bat"]]
The insight: two words are anagrams iff they reduce to the same canonical form. Pick a canonical form, use it as a key, and bucket.
Approach 1 — sorted-string key
Sort each word's letters; anagrams produce identical sorted strings.
function groupAnagrams(strs) {
const map = new Map()
for (const str of strs) {
const key = [...str].sort().join("") // "eat" -> "aet"
if (!map.has(key)) map.set(key, [])
map.get(key).push(str)
}
return [...map.values()]
}Complexity: O(n·k log k) — for n words of length up to k, sorting each word is O(k log k). Space O(n·k) for the groups. Simple and usually good enough.
Approach 2 — character-count key (optimal)
Sorting is the only super-linear step, and we can remove it. Two words are anagrams iff they have the same letter counts, so build a 26-slot count signature instead:
function groupAnagrams(strs) {
const map = new Map()
for (const str of strs) {
const count = new Array(26).fill(0)
for (const ch of str) count[ch.charCodeAt(0) - 97]++
const key = count.join("#") // "#" separator avoids "1,11" vs "11,1" collisions
if (!map.has(key)) map.set(key, [])
map.get(key).push(str)
}
return [...map.values()]
}Complexity: O(n·k) — each word is scanned once, no sort. This is the answer interviewers are usually fishing for once you've given the sorted version.
The # separator in the key matters: [1,11,0,…] and [11,1,0,…] would both join to "1110…" without it, colliding two different signatures. Separate the counts.
Which to use
| Approach | Key | Time | When |
|---|---|---|---|
| Sorted string | letters sorted | O(n·k log k) | Simplest; any character set |
| Count signature | 26 letter counts | O(n·k) | Optimal; lowercase a–z |
Interview follow-ups
- "Can you do better than
O(n·k log k)?" — Yes: the count-key approach atO(n·k). - "What if the input isn't lowercase a–z?" — Drop the fixed 26-slot array for a
Map<char, count>serialised as the key, or use the sorted-string key. - "Why a
Mapover a plain object?" — Either works here; aMapkeeps insertion order and avoids prototype-key edge cases, and[...map.values()]is clean.
More practice
This is one of a set of JavaScript coding interview challenges with worked solutions.
Sources
Key takeaways
- •All anagrams share a canonical form — use it as a hash-map key and group in one pass.
- •Sorted-string key: simple, O(n·k log k) for n words of length k.
- •Character-count key (26 counts): avoids the sort, O(n·k) — the optimal answer interviewers want.
- •Use a Map (or plain object) keyed on the canonical form; return [...map.values()].
Frequently asked questions
What is the time complexity of grouping anagrams?
With a sorted-string key it is O(n·k log k): you sort each of n words of length up to k. With a character-count key it is O(n·k) because you scan each word once instead of sorting it. Space is O(n·k) to store the groups.
Why is the character-count approach faster?
Sorting each word costs O(k log k). Counting the 26 letters costs O(k). Since both approaches touch every character anyway, replacing the per-word sort with a linear count drops the overall bound from O(n·k log k) to O(n·k).
How would you handle Unicode or non-lowercase input?
The fixed 26-slot count assumes lowercase a–z. For arbitrary characters, use a Map from character to count and serialise that as the key, or fall back to the sorted-string key, which works for any character set without a fixed alphabet size.
Software Engineering Leader & Technical Author · Updated August 26, 2026