-
Notifications
You must be signed in to change notification settings - Fork 0
/
tally-votes.js
105 lines (87 loc) · 3.18 KB
/
tally-votes.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Given a collection of users, tally up the votes for a proposed item.
// See https://github.com/Crowdocracy/liquid-api/issues/5 for full db schemas.
'use strict'
var _ = require('lodash')
// -------------------------
// Prep work for example context
// -------------------------
var voters = require('./example-voters.js') // voters.length === 8
// console.log(voters[4])
// // {
// // uid: 'e',
// // full_name: 'Eva Ernst',
// // delegate: 'a'
// // }
var bill = {
uid: 'exampleItem',
name: 'Example Item',
author: 'e', // voter_uid of 'Eva Ernst'
body: '',
date_introduced: new Date('Mon Sep 12 2016 04:34:21 GMT-0700 (PDT)'),
date_of_vote: new Date('Fri Sep 16 2016 17:00:00 GMT-0700 (PDT)'),
votes_yea: 0, // these tally values all default to 0
votes_yea_from_delegate: 0,
votes_nay: 0,
votes_nay_from_delegate: 0,
votes_no_vote: 0,
}
// Generate random votes on exampleItem for all our users
var votes = require('./generate-random-votes.js')(voters)
// Create indices for quick lookups
var votersByUid = _.keyBy(voters, 'uid')
var votesByVoterUid = _.keyBy(votes, 'voter_uid')
// -------------------------
// Now the actual vote-tallying alorithm begins
// -------------------------
// declare cycleState so resolveIndividualsPosition has access
var cycleState
// Given a voter and the record of all votes,
// return that individual's voter position (recursive)
function resolveIndividualsPosition(voter, votesByVoterUid) {
// Did the voter explicitly vote?
if (votesByVoterUid.hasOwnProperty(voter.uid)) {
return votesByVoterUid[voter.uid].position
}
// Protect against endless cycle of no-show votes
cycleState.hare = votersByUid[cycleState.hare.delegate]
if (!votesByVoterUid.hasOwnProperty(cycleState.hare.uid)) {
cycleState.hare = votersByUid[cycleState.hare.delegate]
if (!votesByVoterUid.hasOwnProperty(cycleState.hare.uid)) {
cycleState.tortoise = votersByUid[cycleState.tortoise.delegate]
if (cycleState.hare === cycleState.tortoise) {
return 'no_vote'
}
}
}
// Otherwise inherit their delegate's position
var delegate = votersByUid[voter.delegate]
return resolveIndividualsPosition(delegate, votesByVoterUid)
}
// Tally up the votes by iterating through each voter
voters.forEach(function (voter) {
// reset cycleState to implement Floyd's Cycle-Finding Algorithm
cycleState = {
tortoise: voter,
hare: voter,
}
var position = resolveIndividualsPosition(voter, votesByVoterUid, cycleState)
var isDelegated = !votesByVoterUid.hasOwnProperty(voter.uid)
// increment tally counter for the appropriate key
var tallyKey = 'votes_' + position
if (position !== 'no_vote' && isDelegated) {
tallyKey += '_from_delegate'
}
bill[tallyKey]++
console.log(voter.full_name, tallyKey)
})
console.log('\nbill:', bill)
// Calculate and print the effective final tallies
var finalTally = {
yea: bill.votes_yea + bill.votes_yea_from_delegate,
nay: bill.votes_nay + bill.votes_nay_from_delegate,
voted: voters.length - bill.votes_no_vote, // is there a quorum?
potential_voters: voters.length,
delegated: bill.votes_yea_from_delegate
+ bill.votes_nay_from_delegate,
}
console.log('\nfinalTally:', finalTally)