Skip to content
Astera Technology

Group arbitrary labels without confusing them with object properties

Count labels with Map, including a name inherited by ordinary objects.

Download complete case notes and answer keys ↓

Trace the fixed program

Count the three fixed labels markup, toString and markup. Keep groups in the order their keys first appear.

Complete original program
const labels=['markup','toString','markup'];const counts=new Map();
for(const label of labels)counts.set(label,(counts.get(label)??0)+1);
console.log(JSON.stringify([...counts]));
Exact console output
[["markup",2],["toString",1]]
Values and execution
Input labelCount after inputKey order
markup1markup
toString1markup, toString
markup2markup, toString

Explain the result

Map.get reads stored entries rather than prototype properties. Missing keys yield undefined; ?? supplies zero before the increment. Updating markup does not move its insertion position. Ordinary objects need an explicit own-property or null-prototype strategy instead.

Test the alternative

On an ordinary empty object, toString is inherited and is a function, not a missing counter. Checking its type proves the collision without relying on an engine-specific function string.

Separate finite alternative
const counts={};const value=counts['toString']??0;
console.log(JSON.stringify({own:Object.hasOwn(counts,'toString'),type:typeof value,zero:value===0}));
Alternative output
{"own":false,"type":"function","zero":false}

Work the boundary case

Does the numeric key 1 share a Map entry with the string key "1"?

Show the worked answer

No. These are distinct keys. Map also supports non-string keys; this lesson counts the supplied string labels only.

Fresh boundary program
const counts=new Map([[1,'number'],['1','text']]);
console.log(JSON.stringify({size:counts.size,number:counts.get(1),text:counts.get('1')}));
Boundary output
{"size":2,"number":"number","text":"text"}

Original example files