Back to JavaScript
JavaScript
easy
mid

How would you count the frequency of elements in an array such as a, b, a, c, c, d?

Iterate once, accumulate counts in an object or Map. reduce((acc, x) => { acc[x] = (acc[x]||0)+1; return acc; }, {}). O(n) time, O(k) space. Discuss object vs Map (Map handles non-string keys, preserves insertion order, no prototype collisions).

3 min read·~6 min to think through

Frequency counting is the canonical hash-map warm-up: one pass, accumulate counts.

With reduce into an object

js
function countFrequency(arr) {
  return arr.reduce((acc, item) => {
    acc[item] = (acc[item] || 0) + 1;  // first time → 0, then +1
    return acc;
  }, {});
}
countFrequency(["a", "b", "a", "c", "c", "d"]);
// { a: 2, b: 1, c: 2, d: 1 }

With a Map (the better default)

js
function countFrequency(arr) {
  const freq = new Map();
  for (const item of arr) {
    freq.set(item, (freq.get(item) || 0) + 1);
  }
  return freq;
}

Object vs Map — the talking point

A Map is usually the better choice and worth mentioning why:

  • Any key type — objects, numbers, booleans as real keys. An object coerces all keys to strings (1 and "1" collide).
  • No prototype collisions — a plain object inherits keys like constructor, toString. Counting an array containing "constructor" against {} gives surprising results. Use Object.create(null) if you stick with an object.
  • Preserves insertion order reliably and has a clean .size / iteration API.

For string keys in interview-scale code, an object is fine — but say you'd reach for Map for robustness.

Complexity

  • Time: O(n) — single pass.
  • Space: O(k) — k distinct elements.

Follow-ups to be ready for

  • Most frequent element — track a running max while counting, or sort entries.
  • First non-repeating element — count, then find the first with count 1.
  • Group by a derived key — same pattern, key is fn(item).

The framing

"One pass, accumulate into a hash map — reduce into an object, or a Map. acc[x] = (acc[x] || 0) + 1. O(n) time, O(k) space. I'd note that Map is the safer default: it allows non-string keys and avoids prototype-key collisions you get with a plain object — though for string keys at interview scale an object is fine. From here, most-frequent or first-non-repeating are small variations on the same count map."

Follow-up questions

  • Why might a Map be better than a plain object here?
  • How would you find the most frequent element?
  • How would you find the first non-repeating element?
  • What prototype-key collision could break the object version?

Common mistakes

  • Forgetting the `|| 0` fallback for the first occurrence.
  • Using a plain object and hitting prototype-key collisions (e.g. 'constructor').
  • Two passes when one suffices.
  • Not returning acc from the reduce callback.

Performance considerations

  • O(n) time, O(k) space — optimal; you must look at every element once. Map and object lookups are both ~O(1); Map can be marginally faster for frequent additions of varied keys.

Edge cases

  • Empty array — returns {} / empty Map.
  • Array with elements named like Object.prototype properties.
  • Mixed types where 1 and '1' would collide in an object.
  • NaN as an element (Map handles it as a key; works fine).

Real-world examples

  • Tallying tag/category counts, vote counts, or log-event types.
  • Building a histogram for analytics.

Senior engineer discussion

Seniors write the one-pass reduce instantly, proactively explain why Map beats a plain object (key types, prototype collisions), state O(n)/O(k), and connect it to most-frequent / first-non-repeating / group-by variations.

Related questions