easyJS ES6+#113

Contar lenguajes (count languages)

Prompt

Given an array of developer objects { name, language }, count how many developers use each language. Return an object { language: count }.

Solution

function countLanguages(devs) {
  const counts = Object.create(null)
  devs.forEach(d => { counts[d.language] = (counts[d.language] || 0) + 1 })
  return counts
}
Mentor's take

This is group-and-count — the frequency map applied to a field of objects instead of raw values. It's the in-memory equivalent of SQL's GROUP BY language, COUNT(*), and naming that equivalence in an interview lands well because it shows you recognize the operation, not just the code. One pass, O(n) time, O(k) space for k distinct languages.

The idiomatic core is (counts[key] || 0) + 1: initialize-or-increment in one expression. The || 0 handles the first sighting of a key, where the lookup is undefined and undefined + 1 would be NaN — a bug that then silently propagates because NaN + 1 is still NaN. Modern spelling is (counts[key] ?? 0) + 1; for counting they're equivalent since 0 is only reachable as a real value if you put it there.

The naive alternative: collect unique languages first, then filter().length per language — O(n·k), plus two data structures. reduce into an object is the common one-expression variant; forEach with a mutation-local accumulator reads just as clearly and the mutation never escapes the function, which is the actual immutability boundary that matters.

The accumulator is Object.create(null), not {}, and that is not pedantry: {} inherits from Object.prototype, so a developer whose language is "constructor" makes counts["constructor"] return an inherited function. It's truthy, || 0 never fires, and fn + 1 yields a string — the count silently becomes garbage. Any accumulator keyed by untrusted input wants a null prototype. What you return is a dictionary-shaped object: property access and Object.keys work exactly as before, but it has no inherited methods — no hasOwnProperty, no toString — which is the whole point, since those are what a key like "constructor" was colliding with.

Scaling note: for non-string keys or huge cardinality, Map beats an object entirely — keys keep their type and there's no prototype to dodge. Worth one sentence out loud.

Red flag: counts[d.language]++ without initialization — undefined++ is NaN, and every subsequent increment keeps it NaN. The || 0 isn't decoration; it's the algorithm.

Say it: "It's GROUP BY COUNT in one pass — initialize-or-increment with (counts[key] ?? 0) + 1, over a null-prototype object so a key like 'constructor' can't inherit a function and poison the count."