Define a function that returns a dictionary of how many votes each candidate gets after a voting system

This looks a bit complicated, but the theory is easy. I'm new to Python... Pls help!!!

I'd like to define a function that returns a dictionary of how many votes each candidate gets after a voting system called Instant Run-off Vote(IRV). I'll briefly explain this system.

In IRV, we begin by counting up all the voters' first choices. If no candidate has a majority, what we do is then identify which candidate came in last. We then go find all the ballots with that candidate as the top choice, and then recount these ballots with the second choice instead. If we have a majority now, we stop. But if we still don't have a majority, we again eliminate the last-place candidate and recount their ballots.

There's a visual example in this video that may make things easier to understand.

So for my function (count_irv), the input is a list of ranked ballots. The first in the ballot is voter's first choice, the second is voter's second choice etc. And I want to return a dictionary of how many votes each candidate ends with after counting with IRV. Every candidate in the input list should appear in the returned dictionary.

for example:

>>> count_irv([['NDP'], ['GREEN', 'NDP', 'BLOC'], ['LIBERAL','NDP'], ['LIBERAL'], ['NDP', 'GREEN']['BLOC', 'GREEN', 'NDP'], ['BLOC', 'CPC'], ['LIBERAL', 'GREEN'], ['NDP']])
    {'BLOC': 0, 'CPC': 0, 'GREEN': 0, 'LIBERAL': 3, 'NDP': 5}

Note: In this case, candidates need to be voted as first choice at least 5 times to have a majority. (winner)

What I have tried so far: I converted the input to a dictionary storing how many ballots for which that party was the first choice, for every party represented in all the ballots. I got this:

{'NDP': 3, 'GREEN': 1, 'LIBERAL': 3, 'BLOC': 2, 'CPC': 0}

So 'CPC' is the last one, what I have to do is to eliminate it and go to the second choice of the voter who chose 'CPC' as first one (in this case None). Next, 'GREEN' becomes the last one, we eliminate it by setting its value to zero and go to the second choice and distribute votes to others...... finally, I should get the answer:

{'BLOC': 0, 'CPC': 0, 'GREEN': 0, 'LIBERAL': 3, 'NDP': 5}.

Stop from here because 'NDP' gets 5 votes (has the majority).

sorry, I'm bad at explaining things. I hope you can understand what i meant... Can anyone help me with this function or give me a hint, please? Thank you for your time!

1 answer

  • answered 2019-11-14 02:14 Shawn

    This should work.

    inp = [['NDP'], ['GREEN', 'NDP', 'BLOC'], ['LIBERAL','NDP'], ['LIBERAL'], ['NDP', 'GREEN'],['BLOC', 'GREEN', 'NDP'], ['BLOC', 'CPC'], ['LIBERAL', 'GREEN'], ['NDP']]
    def irv(og_votes):
        og_candidates = {candidate:0 for candidate in set([candidate for vote in og_votes for candidate in vote])}
        votes = og_votes.copy()
        candidates = og_candidates.copy()
        for vote in votes: candidates[vote[0]] += 1
        while not candidates[max(candidates,key=candidates.get)] >= len(og_votes)//2:
            elim = min(candidates,key=candidates.get)
            for ind, vote in enumerate(votes):
                if vote[0] == elim: votes[ind].pop(0)
            votes = [vote for vote in votes if vote]
            candidates = {candidate:0 for candidate in candidates}
            for vote in votes: candidates[vote[0]] += 1
        for candidate in candidates: og_candidates[candidate] += candidates[candidate]
        return og_candidates

    Returns {'CPC': 0, 'LIBERAL': 3, 'NDP': 5, 'BLOC': 0, 'GREEN': 0}

    It flattens the original list of votes and makes it a set, to remove duplicates. If you want a hardcoded list of candidates, replace the first og_candidates line with an object of each candidate set to zero votes.