Skip to content
Skilled.dev

Free lesson walkthrough · TypeScript

Most frequent email in TypeScript: hash-map solution

Count email occurrences and return the unique most frequent address. Follow the TypeScript implementation, its correctness argument, worked example, complexity, and boundary tests.

4 min read · Worked code, edge cases, and complexity

Find the email address that occurs most often in the input. The lesson assumes a unique most frequent address, so ties do not need a policy. Addresses are compared as the supplied strings. For ['a@x.test', 'b@x.test', 'a@x.test', 'c@x.test', 'a@x.test'], return 'a@x.test'.

Count identities instead of comparing positions

A nested search can count each address by scanning the whole list again, but repeated addresses repeat the same work. A frequency table stores the count for each distinct address. Read an address, create a zero count if needed, and increment its count. The table's keys are email strings, and its values are occurrence counts.

After any prefix of the input, each table entry equals the number of appearances of that key in the prefix. An update affects exactly the incoming key, so it preserves all other counts. Once the scan ends, the table contains enough information to answer the question without revisiting the original list.

Choose the largest completed count

Scan the table entries while keeping a current winning address and its count. Replace the winner only when an entry has a larger count. Every skipped entry has count no greater than the retained winner, so after the final entry the retained address has the greatest frequency.

For the example, the completed counts are three for a@x.test and one each for the other addresses. The answer is independent of whether the winning address appears first, last, or in scattered positions. A shortcut based on adjacent duplicates fails when its copies are separated by other addresses.

Separate the promise from a majority vote

Most frequent does not necessarily mean more than half of the input. A list can contain four copies of one address and three copies each of two others. Four is the unique maximum, although it is less than half of ten. A majority-vote algorithm requires a stronger promise, or a verification pass and additional reasoning.

The unique-maximum promise also explains why table iteration order does not affect the result. If you remove that promise, specify a tie rule before coding. First encountered, lexicographically smallest, and arbitrary tied winner are different contracts. Updating on >= instead of > can change the winner under a tie without solving that ambiguity.

Treat input text consistently

Do not lowercase, trim, or otherwise normalize addresses unless the contract asks for that behavior. Normalization changes which strings count as equal. This task is a counting exercise over supplied identifiers, not an email-delivery validation system. The table must preserve those exact identifiers through counting and return the original winning string.

A general string-keyed implementation should use a real map or a dictionary with safe key semantics. Some languages' ordinary objects have inherited property names. Valid email-shaped inputs avoid many such collisions, but a reusable counter over arbitrary strings should not rely on that accident. Also avoid sorting the input merely to group copies when a count table is sufficient.

Complexity and useful tests

For n addresses and u distinct keys, the table stores O(u) entries. Counting and finding the maximum take expected O(n + u) table operations. If string lengths vary, include hashing and equality work: the scan must read the characters in the keys. The shorthand O(n) assumes key handling has an appropriate bounded or amortized cost.

Test one address, an address with every copy adjacent, a winner spread across the whole list, and a winner that first appears last. Add the four-versus-three-versus-three example to reject an unjustified majority shortcut. For generated tests, create a unique largest frequency deliberately, shuffle the copies, and compare with a separately sorted frequency list. A streaming follow-up can update the current winner during counting, avoiding the final table scan while retaining the same table storage.

TypeScript implementation

The frequency table receives dynamically computed keys. A stricter standalone TypeScript project can annotate it as Record<string, number>.

The TypeScript annotations describe the course input contract; they do not validate arbitrary JSON at runtime. Numeric values use JavaScript numbers, including the same safe integer limit. Array and object mutations are visible through shared references. Keep the declared entry name and return shape when moving the implementation into the lesson editor.

const findHackerEmail = (emails: string[]): string => {
  const emailCountTable = {};
  let maxCount = 0;
  let hackerEmail = '';

  // Create a hash table that tracks the count of each email
  // This is an O(n) operation
  for (const email of emails) {
    // If we haven't seen the email yet, add it to the table
    if (!emailCountTable[email]) {
      emailCountTable[email] = 0;
    }

    // Increment the count for the email
    emailCountTable[email]++;
  }

  // Iterate the object to find the email that appears the most
  // This is an O(n) operation
  for (const email in emailCountTable) {
    const count = emailCountTable[email];

    if (count > maxCount) {
      maxCount = count;
      hackerEmail = email;
    }
  }

  return hackerEmail;
};

Practice this implementation

Open Catch the Hacker to run the implementation against the course tests, then change one boundary condition and explain the first failing case. The editor supplies the starter signature and any node types or helper APIs used above. Copy the implementation into that environment; the snippet is the solution body, without the input parser or test driver. Your saved progress belongs to the original lesson.

For more TypeScript conventions and practice problems, use the TypeScript interview guide.

Try the exercise

Write your own implementation in the course editor. This walkthrough belongs to the same lesson; your course progress stays with that exercise. Exercise access follows the course’s access settings.