Skip to content

Instantly share code, notes, and snippets.

@mcsf
Last active October 6, 2015 18:05
Show Gist options
  • Select an option

  • Save mcsf/2d43ef5c33198522b22f to your computer and use it in GitHub Desktop.

Select an option

Save mcsf/2d43ef5c33198522b22f to your computer and use it in GitHub Desktop.
Following the 2015 Portuguese legislative elections, a comparison between actual seat allocation and simulations of nationwide (i.e. ignoring electoral districts) application of d'Hondt and Sainte-Laguë methods.
/**
* Following the 2015 Portuguese legislative elections, a comparison between
* actual seat allocation and simulations of nationwide (i.e. ignoring
* electoral districts) application of d'Hondt and Sainte-Laguë methods.
*
* The last three column reflect parliamentary seat assignments.
*
* - "Atribuídos" are the actual seats that were assigned following the
* elections
*
* - "Sob d'Hondt" are the seats that would have been assigned if the
* electoral system abandoned electoral districts in favor of nationwide
* assignment.
*
* - "Sob Sainte-Laguë" are the seats that would have been assigned by
* abandoning districts and switching to the Sainte-Laguë assignment
*
*
* Partido Votos Atribuídos Sob d'Hondt Sob Sainte-Laguë
* -------------- ------- ---------- ----------- ----------------
* PPD/PSD.CDS-PP 1979132 99 90 86
* PS 1740280 85 79 75
* BE 549153 19 24 24
* PCP-PEV 444319 17 20 19
* PPD/PSD 81054 5 3 4
* PAN 74656 1 3 3
* PDR 60912 0 2 3
* PCTP/MRPP 59812 0 2 3
* L/TDA 38958 0 1 2
* PNR 27104 0 1 1
* MPT 22384 0 1 1
* PTP-MAS 20690 0 0 1
* NC 18695 0 0 1
* PPM 14799 0 0 1
* JPP 14196 0 0 1
* PURP 13739 0 0 1
* CDS-PP 7536 0 0 0
* CDS-PP.PPM 3654 0 0 0
* PPV/CDC 2658 0 0 0
* PTP 1748 0 0 0
*
*
* The source-code that generated the table follows.
*/
import R from 'ramda';
import Table from 'easy-table';
// data Party = (Number, Number)
//
// A Party is a pair of:
// - number of votes
// - number of allocated seats
// quotSainteLague :: Party -> Number
const quotSainteLague = ([v, s]) => v / (2 * s + 1);
// quotDHondt :: Party -> Number
const quotDHondt = ([v, s]) => v / (s + 1);
// compareAssignments :: Number -> [Number] -> [(Number, Number, Number)]
const compareAssignments = (maxSeats, votes) =>
R.zipWith(
(a, b) => [a[0], a[1], b[1]],
assignSeats(quotSainteLague, maxSeats, votes),
assignSeats(quotDHondt, maxSeats, votes)
);
// assignSeats :: Function -> Number -> [Number] -> [Party]
const assignSeats = (quot, maxSeats, votes) =>
R.repeat(null, maxSeats).reduce(nextRound(quot), votes.map(v => [v, 0]));
// nextRound :: Function -> [Party] -> [Party]
const nextRound = R.curry((quot, ps) => {
const i = nextWinner(quot, ps);
const p = [ ps[i][0], ps[i][1] + 1 ];
return replaceAtIndex(i, p, ps);
});
// nextWinner :: Function -> [Party] -> Number
const nextWinner = (quot, ps) => indexOfMax(ps.map(quot));
// indexOfMax :: [Number] -> Number
const indexOfMax = R.converge(R.indexOf, R.apply(Math.max), R.identity);
// replaceAtIndex :: Number -> a -> [a] -> [a]
const replaceAtIndex = (index, x, xs) =>
[ ...xs.slice(0, index), x, ...xs.slice(index + 1) ];
const compare2015 = () => {
const seats = 226;
const votes = [
[ 1979132, 'PPD/PSD.CDS-PP', 99 ],
[ 1740280, 'PS', 85 ],
[ 549153, 'BE', 19 ],
[ 444319, 'PCP-PEV', 17 ],
[ 81054, 'PPD/PSD', 5 ],
[ 74656, 'PAN', 1 ],
[ 60912, 'PDR', 0 ],
[ 59812, 'PCTP/MRPP', 0 ],
[ 38958, 'L/TDA', 0 ],
[ 27104, 'PNR', 0 ],
[ 22384, 'MPT', 0 ],
[ 20690, 'PTP-MAS', 0 ],
[ 18695, 'NC', 0 ],
[ 14799, 'PPM', 0 ],
[ 14196, 'JPP', 0 ],
[ 13739, 'PURP', 0 ],
[ 7536, 'CDS-PP', 0 ],
[ 3654, 'CDS-PP.PPM', 0 ],
[ 2658, 'PPV/CDC', 0 ],
[ 1748, 'PTP', 0 ],
];
console.log(Table.print(
compareAssignments(seats, votes.map(R.nth(0)))
.map((line, i) => ({
'Partido': votes[i][1],
'Votos': line[0],
'Atribuídos': votes[i][2],
'Sob d\'Hondt': line[2],
'Sob Sainte-Laguë': line[1],
}))
));
};
compare2015();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment