ApprovalElection<TCandidate extends Comparable> constructor

ApprovalElection<TCandidate extends Comparable>(
  1. List<ApprovalBallot<TCandidate>> ballots, {
  2. Iterable<TCandidate>? candidates,
})

Implementation

factory ApprovalElection(
  List<ApprovalBallot<TCandidate>> ballots, {
  Iterable<TCandidate>? candidates,
}) {
  var candidateVotes = <TCandidate, int>{};

  for (var ballot in ballots) {
    for (var candidate in ballot.choices) {
      candidateVotes[candidate] = (candidateVotes[candidate] ?? 0) + 1;
    }
  }

  if (candidates != null) {
    assert(
      candidateVotes.keys.every(candidates.contains),
      'If `candidates` is provided, then every candidate in `ballots` should '
      'exist in `candidates`.',
    );
    for (var candidate in candidates) {
      candidateVotes.putIfAbsent(candidate, () => 0);
    }
  }

  candidateVotes = {
    for (var entry
        in candidateVotes.entries.toList()
          ..sort((a, b) => a.key.compareTo(b.key)))
      entry.key: entry.value,
  };

  final groups = groupBy(candidateVotes.keys, (c) => candidateVotes[c]!)
      .entries
      .toList(growable: false)
        // NOTE: reverse sorting
        ..sort((a, b) => b.key.compareTo(a.key));

  var place = 1;
  final places = <PluralityElectionPlace<TCandidate>>[];
  for (var count in groups) {
    final p = PluralityElectionPlace(place, count.value, count.key);
    places.add(p);
    place += p.length;
  }

  return ApprovalElection._internal(
    ballots,
    candidateVotes.keys.toList(growable: false),
    places,
  );
}