npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

condorcet-winner

v1.1.1

Published

A simple condorcet vote algorithm implementation in typescript

Readme

condorcet-winner

A TypeScript library implementing two major ranked voting methods: Condorcet method and Borda count. Compare results, understand voting paradoxes, and choose the right method for your use case.

Installation

yarn add condorcet-winner

or

npm install condorcet-winner

Quick Start

import { condorcetWinner, bordaWinner } from 'condorcet-winner';

const candidates = ['Alice', 'Bob', 'Charlie'];
const ballots = [
  ['Alice', 'Bob', 'Charlie'],
  ['Bob', 'Charlie', 'Alice'],
  ['Alice', 'Charlie', 'Bob'],
];

// Condorcet method: head-to-head comparisons
const condorcet = condorcetWinner(candidates, ballots);
console.log('Condorcet winner:', condorcet); // 'Alice'

// Borda count: ranking-based points
const borda = bordaWinner(candidates, ballots);
console.log('Borda winner:', borda); // 'Alice'

API Reference

condorcetWinner(candidates, ballots)

Determines the winner using the Condorcet method (pairwise comparisons).

How it works:

  • Compares every pair of candidates head-to-head
  • A candidate wins if they beat all others in majority vote
  • Returns null if no Condorcet winner exists (Condorcet paradox)

Parameters:

  • candidates: string[] - Array of candidate identifiers
  • ballots: string[][] - Array of ranked ballots (ordered by preference)

Returns: string | null - The Condorcet winner, or null if paradox

Example:

const winner = condorcetWinner(
  ['A', 'B', 'C'],
  [
    ['A', 'B', 'C'],
    ['B', 'C', 'A'],
    ['C', 'A', 'B']
  ]
);
// Returns null (rock-paper-scissors paradox)

bordaWinner(candidates, ballots)

Determines the winner using the Borda count method (ranking points).

How it works:

  • Assigns points based on ranking position (1st place = n points, 2nd = n-1, etc.)
  • Sums all points across ballots
  • Candidate with highest total wins
  • Returns null only in case of exact tie

Parameters:

  • candidates: string[] - Array of candidate identifiers
  • ballots: string[][] - Array of ranked ballots (ordered by preference)

Returns: string | null - The Borda winner, or null if exact tie

Example:

const winner = bordaWinner(
  ['A', 'B', 'C'],
  [
    ['A', 'B', 'C'], // A:3pts, B:2pts, C:1pt
    ['B', 'C', 'A'], // B:3pts, C:2pts, A:1pt
    ['A', 'C', 'B']  // A:3pts, C:2pts, B:1pt
  ]
);
// Returns 'A' (7 points total)

When to Use Each Method

Use Condorcet when:

  • ✅ You want the "majority preferred" winner
  • ✅ Head-to-head matchup matters most
  • ✅ You can handle potential paradoxes (no winner)
  • ✅ You need to detect voting cycles

Example use cases: Political elections, executive decisions, board voting

Use Borda when:

  • ✅ You want a consensus candidate
  • ✅ You need a guaranteed winner (no paradoxes)
  • ✅ Overall ranking preferences matter
  • ✅ You want to consider "intensity" of preferences

Example use cases: Award voting, rankings, preference aggregation

Key Differences

| Feature | Condorcet | Borda | |---------|-----------|-------| | Winner selection | Beats all others head-to-head | Highest total points | | Always has winner? | ❌ No (paradox possible) | ✅ Yes (unless exact tie) | | Considers full ranking | ❌ Only pairwise | ✅ Yes (all positions) | | Majority criterion | ✅ Yes | ❌ No | | Consensus-oriented | ❌ No | ✅ Yes |

When Condorcet ≠ Borda

The methods can produce different winners. Here's a real example:

const candidates = ['A', 'B', 'C', 'D'];
const ballots = [
  // 8 voters
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'C', 'D'],
  ['A', 'B', 'C', 'D'],
  // 4 voters
  ['B', 'C', 'D', 'A'],
  ['B', 'C', 'D', 'A'],
  ['B', 'C', 'D', 'A'],
  ['B', 'C', 'D', 'A'],
  // 2 voters
  ['C', 'D', 'A', 'B'],
  ['C', 'D', 'A', 'B'],
  // 1 voter
  ['D', 'A', 'B', 'C'],
];

condorcetWinner(candidates, ballots); // Returns 'A'
bordaWinner(candidates, ballots);     // Returns 'B'

Why the difference?

  • A beats every other candidate head-to-head (Condorcet winner)
  • B has the highest total points across all rankings (Borda winner)

This demonstrates that Borda rewards candidates who are ranked consistently high, while Condorcet rewards candidates who win direct matchups.

Input Validation

Both methods validate inputs and throw descriptive errors:

// Empty candidates
condorcetWinner([], ballots); // Error: "Candidates array cannot be empty"

// Empty ballots
bordaWinner(candidates, []); // Error: "Ballots array cannot be empty"

Incomplete Ballots

Both methods handle incomplete ballots (voters who don't rank all candidates):

const candidates = ['A', 'B', 'C'];
const ballots = [
  ['A', 'B'],    // C not ranked
  ['B'],         // Only ranks B
  ['A', 'C']     // B not ranked
];

// Both methods handle this gracefully
condorcetWinner(candidates, ballots); // Works
bordaWinner(candidates, ballots);     // Works

TypeScript Support

Full TypeScript support with type definitions included:

function condorcetWinner(
  candidates: string[],
  ballots: string[][]
): string | null;

function bordaWinner(
  candidates: string[],
  ballots: string[][]
): string | null;

Algorithm Complexity

  • Condorcet: O(n² × m) where n = candidates, m = ballots
  • Borda: O(n × m) where n = candidates, m = ballots

Both are efficient for typical election sizes (< 100 candidates, < 10,000 ballots).

Contributing

Contributions welcome! Please ensure:

  • ✅ Tests pass (yarn test)
  • ✅ Coverage stays at 100% (yarn test --coverage)
  • ✅ Code is properly formatted (yarn lint)
  • ✅ Build succeeds (yarn build)

License

MIT

References