Transitioning to TypeScript for Enhanced Voting System Design

Introduction

Welcome back to a fascinating session where we will learn about enhancing existing functionality without causing regressions. Our scenario today involves designing a voting system. We'll start with the basic implementation of the voting system and gradually introduce additional elements of complexity.

Starter Task Review

In our initial task, we create a simple voting system in TypeScript with a set of basic functionalities. Let's define the methods with TypeScript type annotations:

  • registerCandidate(candidateId: string): boolean: This method is used for adding new candidates to our system.
  • vote(timestamp: number, voterId: string, candidateId: string): boolean: This method is designed to facilitate users in casting their votes. Each vote is given a timestamp.
  • getVotes(candidateId: string): number | null: This method retrieves the total number of votes for a given candidate.
  • topNCandidates(n: number): string[]: We also want to add a leaderboard functionality to our system. This method returns the top n candidates sorted by the number of votes.

Initial Solution Development

Let's jump into the TypeScript code and begin the implementation of our starter task. Here, we use TypeScript's type system to define class fields and method parameters, providing more robust type-checking and better code maintenance.

class VotingSystem {
  private candidates: Map<string, number>; // Stores candidateId as key and votes as value
  private voters: Map<string, { votes: string[]; timestamps: number[] }>; // Tracks each voter's voting history
  private blockTime?: number; // Optional timestamp for blocking voter registration

  constructor() {
    this.candidates = new Map<string, number>();
    this.voters = new Map<string, { votes: string[]; timestamps: number[] }>();
  }

  registerCandidate(candidateId: string): boolean {
    if (this.candidates.has(candidateId)) {
      return false; // Candidate is already registered
    }
    this.candidates.set(candidateId, 0); // Initialize candidates with 0 votes
    return true;
  }

  vote(timestamp: number, voterId: string, candidateId: string): boolean {
    // Check if blockTime is set and if the vote attempt is after the block timestamp
    if (this.blockTime && timestamp >= this.blockTime) {
      return false; // Vote attempt is blocked due to the registration freeze
    }

    if (!this.candidates.has(candidateId)) {
      return false; // Returns false if candidate is not registered
    }

    if (!this.voters.has(voterId)) {
      this.voters.set(voterId, { votes: [], timestamps: [] });
    }

    const voterData = this.voters.get(voterId)!;
    voterData.votes.push(candidateId); // Record the vote
    voterData.timestamps.push(timestamp); // Record the time of the vote
    this.candidates.set(candidateId, this.candidates.get(candidateId)! + 1); // Increment vote count for the candidate
    return true;
  }

  getVotes(candidateId: string): number | null {
    return this.candidates.get(candidateId) || null; // Retrieve vote count for a candidate, or null if not found
  }

  topNCandidates(n: number): string[] {
    return Array.from(this.candidates.keys())
      .sort((a, b) => this.candidates.get(b)! - this.candidates.get(a)!)
      .slice(0, n); // Return top `n` candidates based on votes
  }
}
Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal