InterviewsVector

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

ApproachKeyTimeWhen
Sorted stringletters sortedO(n·k log k)Simplest; any character set
Count signature26 letter countsO(n·k)Optimal; lowercase a–z

Interview follow-ups

  • "Can you do better than O(n·k log k)?" — Yes: the count-key approach at O(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 Map over a plain object?" — Either works here; a Map keeps 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.

By Mohammad Wasi

Software Engineering Leader & Technical Author · Updated August 26, 2026


Related Posts