|
#!/usr/bin/env node |
|
|
|
const _ = require('lodash'); |
|
|
|
const COMMITTEE_SIZE = 34; |
|
|
|
const PARTIES = { |
|
cdu: { |
|
seats: 311 |
|
}, |
|
spd: { |
|
seats: 193 |
|
}, |
|
left: { |
|
seats: 64 |
|
}, |
|
greens: { |
|
seats: 63 |
|
} |
|
}; |
|
|
|
Object.defineProperty(PARTIES, 'seatsTotal', { |
|
get: () => { |
|
let seatsTotal = 0; |
|
for(let k in PARTIES) { |
|
seatsTotal += PARTIES[k].seats || 0; |
|
} |
|
return seatsTotal; |
|
} |
|
}); |
|
|
|
((...args) => { |
|
|
|
let [committeeSize, parties] = args; |
|
/** |
|
* Generates Saint Lague/Schepers distribution |
|
* @return {[type]} [description] |
|
*/ |
|
const getSLS = (committeeSize, parties) => { |
|
console.log('Generating SLS distribution for a'); |
|
console.log('committee with', committeeSize, 'seats.'); |
|
console.log('Eligable parties: ', _.keys(parties)); |
|
|
|
console.log('Calculting ranks...'); |
|
// add ranks to parties |
|
let rankedParties = _.mapValues(parties, (party) => { |
|
party.ranks = calculateRanks(committeeSize, parties.seatsTotal, party); |
|
return party; |
|
}); |
|
|
|
let ranks = _.reduce(rankedParties, (ranks, party, key) => { |
|
ranks[key] = party.ranks; |
|
return ranks; |
|
}, {}); |
|
|
|
console.log('Comparing ranks...'); |
|
let partyKeys = _.keys(ranks); |
|
let committeeSeats = _.zipObject(partyKeys, new Array(partyKeys.length).fill(0)); |
|
for (let i = 0; i < committeeSize; i++) { |
|
let smallest = determineSmallestRank(ranks); |
|
ranks = smallest.ranks; |
|
committeeSeats[smallest.key]++; |
|
} |
|
|
|
console.log('Done.'); |
|
console.log('Your comiittee\'s seat distribution is', committeeSeats); |
|
}; |
|
|
|
const determineSmallestRank = (ranks) => { |
|
// find smallest and its key |
|
let smallest = Infinity; |
|
let smallestKey = ''; |
|
|
|
for (let key in ranks) { |
|
let probe = ranks[key][0]; |
|
if (probe < smallest) { |
|
smallest = probe; |
|
smallestKey = key; |
|
} |
|
} |
|
|
|
if (smallest < Infinity) { |
|
// remove smallest from array under key |
|
ranks[smallestKey].shift(); |
|
} |
|
|
|
return { ranks: ranks, key: smallestKey }; |
|
}; |
|
|
|
const calculateRanks = (committeeSize, seatsTotal, party) => { |
|
let incrementalFactor = 0.5; |
|
let seatsFactor = seatsTotal / party.seats; |
|
// - number of ranks === total seats in committee, to be safe |
|
// (although that scenario would mean a new dictator ^_^) |
|
// - first rank wants 0.5, so we 'increment after' |
|
// - example uses 3 decimals |
|
// round(rank * 1000) / 1000 |
|
// round(12.1839384 * 1000 [=12183.9384]) [= 12184] / 1000 = 12.184 |
|
return new Array(committeeSize).fill(0) |
|
.map(() => Math.round((seatsFactor * incrementalFactor++) * 1000) / 1000); |
|
}; |
|
|
|
getSLS(committeeSize, parties); |
|
|
|
})(COMMITTEE_SIZE, PARTIES); |