linted and refactored
This commit is contained in:
parent
005eed7d38
commit
fcb49c3666
2 changed files with 170 additions and 155 deletions
|
@ -1,14 +1,16 @@
|
|||
|
||||
define([], function () {
|
||||
var Condorcet = {}
|
||||
var Condorcet = {};
|
||||
|
||||
// Creates every possible combination pair of given options
|
||||
var getPermutations = function(array, size) {
|
||||
|
||||
var result = [];
|
||||
|
||||
function p(t, i) {
|
||||
if (t.length === size) {
|
||||
result.push(t);
|
||||
result.push(t.slice().reverse())
|
||||
result.push(t.slice().reverse());
|
||||
return;
|
||||
}
|
||||
if (i >= array.length) {
|
||||
|
@ -18,118 +20,120 @@ define([], function () {
|
|||
p(t, i + 1);
|
||||
}
|
||||
|
||||
var result = [];
|
||||
p([], 0);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
Condorcet.showCondorcetWinner = function(_answers, opts, uid, form) {
|
||||
Condorcet.showCondorcetWinner = function(_answers, opts, uid, form, optionArray, listOfLists) {
|
||||
|
||||
var comparePairs = function() {
|
||||
var pairs = getPermutations(optionArray, 2)
|
||||
var pairs = getPermutations(optionArray, 2);
|
||||
|
||||
var pairDict = {}
|
||||
var pairDict = {};
|
||||
pairs.forEach(function (pair) {
|
||||
pairDict[pair] = 0
|
||||
pairDict[pair] = 0;
|
||||
listOfLists.forEach(function(optionList) {
|
||||
if (optionList.indexOf(pair[0]) < optionList.indexOf(pair[1])) {
|
||||
pairDict[pair] ++
|
||||
pairDict[pair] ++;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
pathDictionary = {}
|
||||
var pathDictionary = {};
|
||||
//Adds winner of each pairwise comparison to path
|
||||
optionArray.forEach(function(option1) {
|
||||
optionArray.forEach(function(option2){
|
||||
if (option1 != option2) {
|
||||
var key1 = [option1, option2].join()
|
||||
var key2 = [option2, option1].join()
|
||||
if (option1 !== option2) {
|
||||
var key1 = [option1, option2].join();
|
||||
var key2 = [option2, option1].join();
|
||||
if (pairDict[key1] > pairDict[key2]) {
|
||||
pathDictionary[key1] = pairDict[key1]
|
||||
pathDictionary[key1] = pairDict[key1];
|
||||
} else if (pairDict[key2] > pairDict[key1]) {
|
||||
pathDictionary[key2] = pairDict[key2]
|
||||
pathDictionary[key2] = pairDict[key2];
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
return pathDictionary
|
||||
}
|
||||
});
|
||||
});
|
||||
return pathDictionary;
|
||||
};
|
||||
|
||||
var schulzeMethod = function(optionArray, listOfLists, _answers, uid) {
|
||||
var schulzeMethod = function(optionArray) {
|
||||
|
||||
var findWeakestPath = function(optionArray) {
|
||||
var pathDictionary = comparePairs(optionArray)
|
||||
var pathDictionary = comparePairs(optionArray);
|
||||
|
||||
//Iterates through paths between two options comparing the weakest edge to current score
|
||||
optionArray.forEach(function(option1) {
|
||||
optionArray.forEach(function(option2) {
|
||||
if (option1 != option2) {
|
||||
if (option1 !== option2) {
|
||||
optionArray.forEach(function(option3){
|
||||
if (option1 != option3 && option2 != option3) {
|
||||
var key1 = [option2, option3].join()
|
||||
var key2 = [option2, option1].join()
|
||||
var key3 = [option1, option3].join()
|
||||
if (option1 !== option3 && option2 !== option3) {
|
||||
var key1 = [option2, option3].join();
|
||||
var key2 = [option2, option1].join();
|
||||
var key3 = [option1, option3].join();
|
||||
var score;
|
||||
if (pathDictionary[key1] === undefined) {
|
||||
var score = 0
|
||||
score = 0;
|
||||
} else {
|
||||
var score = pathDictionary[key1]
|
||||
score = pathDictionary[key1];
|
||||
}
|
||||
var path1;
|
||||
if (pathDictionary[key2] !== undefined) {
|
||||
var path1 = pathDictionary[key2]
|
||||
path1 = pathDictionary[key2];
|
||||
} else {
|
||||
var path1 = 0
|
||||
path1 = 0;
|
||||
}
|
||||
var path2;
|
||||
if (pathDictionary[key3]) {
|
||||
var path2 = pathDictionary[key3]
|
||||
path2 = pathDictionary[key3];
|
||||
} else {
|
||||
var path2 = 0
|
||||
path2 = 0;
|
||||
}
|
||||
var weakestPath = Math.min(path1, path2)
|
||||
var weakestPath = Math.min(path1, path2);
|
||||
if (weakestPath > score) {
|
||||
pathDictionary[key1] = weakestPath
|
||||
pathDictionary[key1] = weakestPath;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
return pathDictionary
|
||||
}
|
||||
});
|
||||
});
|
||||
return pathDictionary;
|
||||
};
|
||||
|
||||
var calculateWinner = function(_answers, uid) {
|
||||
var calculateWinner = function() {
|
||||
|
||||
var pathDictionary = findWeakestPath(optionArray)
|
||||
var pathDictionary = findWeakestPath(optionArray);
|
||||
//Calculate scores for each option and select winner
|
||||
winningMatches = {}
|
||||
var winningMatches = {};
|
||||
optionArray.forEach(function(option){
|
||||
winningMatches[option] = 0
|
||||
})
|
||||
winningMatches[option] = 0;
|
||||
});
|
||||
Object.keys(pathDictionary).forEach(function(pair) {
|
||||
option1 = pair.split(',')[0]
|
||||
winningMatches[option1] ++
|
||||
})
|
||||
var option1 = pair.split(',')[0];
|
||||
winningMatches[option1] ++;
|
||||
});
|
||||
|
||||
rankedResults = {}
|
||||
var rankedResults = {};
|
||||
Object.keys(winningMatches).forEach(function(option) {
|
||||
if (Object.keys(rankedResults).includes(winningMatches[option].toString())) {
|
||||
rankedResults[winningMatches[option]].push(option)
|
||||
rankedResults[winningMatches[option]].push(option);
|
||||
} else {
|
||||
rankedResults[winningMatches[option]] = [option]
|
||||
rankedResults[winningMatches[option]] = [option];
|
||||
}
|
||||
})
|
||||
return [rankedResults, winningMatches]
|
||||
}
|
||||
return(calculateWinner(_answers, uid))
|
||||
}
|
||||
});
|
||||
return [rankedResults, winningMatches];
|
||||
};
|
||||
return(calculateWinner());
|
||||
};
|
||||
|
||||
var rankedPairsMethod = function (optionArray, listOfLists, _answers) {
|
||||
|
||||
var pairs = getPermutations(optionArray, 2)
|
||||
var pairs = getPermutations(optionArray, 2);
|
||||
|
||||
var lockPairs = function() {
|
||||
//'Locks' pairwise comparisons which do not create a beatpath cycle
|
||||
var pathDictionary = comparePairs(listOfLists, pairs)
|
||||
var pathDictionary = comparePairs(listOfLists, pairs);
|
||||
|
||||
var items = Object.keys(pathDictionary).map(function(key) {
|
||||
return [key, pathDictionary[key]];
|
||||
|
@ -139,37 +143,37 @@ define([], function () {
|
|||
return second[1] - first[1];
|
||||
});
|
||||
|
||||
list = [items[0][0]]
|
||||
list2 = []
|
||||
var list = [items[0][0]];
|
||||
var list2 = [];
|
||||
items.forEach(function(item){
|
||||
opts = item[0].split(',')
|
||||
opts = item[0].split(',');
|
||||
if (!list2.includes(opts[0])) {
|
||||
list2.push(opts[1])
|
||||
list2.push(opts[1]);
|
||||
if (!list.includes(item[0])) {
|
||||
list.push(item[0])
|
||||
list.push(item[0]);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return list
|
||||
return list;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
var rankPairs = function(optionArray, listOfLists, _answers) {
|
||||
//Ranks locked pairs
|
||||
var list = lockPairs(optionArray, listOfLists, _answers)
|
||||
var list = lockPairs(optionArray, listOfLists, _answers);
|
||||
|
||||
rankDict = {}
|
||||
var rankDict = {};
|
||||
optionArray.forEach(function(o){
|
||||
rankDict[o] = 0
|
||||
rankDict[o] = 0;
|
||||
list.forEach(function(pair) {
|
||||
if (o == pair.split(',')[1]) {
|
||||
rankDict[o] --
|
||||
} else if (o == pair.split(',')[0]) {
|
||||
rankDict[o] ++
|
||||
if (o === pair.split(',')[1]) {
|
||||
rankDict[o] --;
|
||||
} else if (o === pair.split(',')[0]) {
|
||||
rankDict[o] ++;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
var rankedItems = Object.keys(rankDict).map(function(key) {
|
||||
return [key, rankDict[key]];
|
||||
|
@ -179,39 +183,39 @@ define([], function () {
|
|||
return second[1] - first[1];
|
||||
});
|
||||
|
||||
finalRank = []
|
||||
var finalRank = [];
|
||||
rankedItems.forEach(function(i){
|
||||
finalRank.push(i[0])
|
||||
})
|
||||
sortedRankDict = {}
|
||||
finalRank.push(i[0]);
|
||||
});
|
||||
var sortedRankDict = {};
|
||||
finalRank.forEach(function(opt){
|
||||
sortedRankDict[opt] = rankDict[opt]
|
||||
})
|
||||
sortedRankDict[opt] = rankDict[opt];
|
||||
});
|
||||
|
||||
return [finalRank, sortedRankDict]
|
||||
}
|
||||
return [finalRank, sortedRankDict];
|
||||
};
|
||||
|
||||
var condorcetWinner = rankPairs(optionArray, listOfLists, _answers)
|
||||
return condorcetWinner
|
||||
}
|
||||
var condorcetWinner = rankPairs(optionArray, listOfLists, _answers);
|
||||
return condorcetWinner;
|
||||
};
|
||||
|
||||
var pickMethod = function(optionArray, listOfLists, _answers, uid){
|
||||
var condorcetWinner = []
|
||||
var schulzeWinner = schulzeMethod(optionArray, listOfLists, _answers, uid)
|
||||
var rankedPairWinner = rankedPairsMethod(optionArray, listOfLists)
|
||||
var method = form[uid].condorcet["method"]
|
||||
if (method == "schulze") {
|
||||
condorcetWinner[0] = schulzeWinner
|
||||
condorcetWinner[1] = rankedPairWinner
|
||||
} else if (method == "ranked") {
|
||||
condorcetWinner[0] = rankedPairWinner
|
||||
condorcetWinner[1] = schulzeWinner
|
||||
var condorcetWinner = [];
|
||||
var schulzeWinner = schulzeMethod(optionArray, listOfLists, _answers, uid);
|
||||
var rankedPairWinner = rankedPairsMethod(optionArray, listOfLists);
|
||||
var method = form[uid].condorcet["method"];
|
||||
if (method === "schulze") {
|
||||
condorcetWinner[0] = schulzeWinner;
|
||||
condorcetWinner[1] = rankedPairWinner;
|
||||
} else if (method === "ranked") {
|
||||
condorcetWinner[0] = rankedPairWinner;
|
||||
condorcetWinner[1] = schulzeWinner;
|
||||
}
|
||||
return condorcetWinner
|
||||
}
|
||||
return pickMethod(optionArray, listOfLists, _answers, uid)
|
||||
}
|
||||
return condorcetWinner;
|
||||
};
|
||||
return pickMethod(optionArray, listOfLists, _answers, uid);
|
||||
};
|
||||
|
||||
return Condorcet
|
||||
})
|
||||
return Condorcet;
|
||||
});
|
||||
|
||||
|
|
|
@ -72,9 +72,20 @@ define([
|
|||
ChainPad,
|
||||
Share, Access, Properties,
|
||||
Flatpickr,
|
||||
Sortable,
|
||||
Sortable
|
||||
)
|
||||
{
|
||||
{
|
||||
Messages.form_chooseCondorcetMethod = "Choose Condorcet winner using: "; //XXX;
|
||||
Messages.form_chooseCondorcetDisplay = "Condorcet winner display view: ";
|
||||
Messages.form_schulzeMethod = "Schulze method";
|
||||
Messages.form_rankedPairs = "Ranked pairs method";
|
||||
Messages.form_condorcetBasicDisplayButton = "Basic display";
|
||||
Messages.form_condorcetExtendedDisplayButton = "Extended display";
|
||||
Messages.form_showCondorcetWinner = "The Condorcet winner is: ";
|
||||
Messages.form_condorcetExtendedDisplay = "Number of matches won by each candidate: ";
|
||||
Messages.form_showAlternateWinnerButton = "Show alternate method winner";
|
||||
Messages.form_showAlternateWinner = "The alternate method Condorcet winner is: ";
|
||||
|
||||
var APP = window.APP = {
|
||||
blocks: {}
|
||||
};
|
||||
|
@ -2435,20 +2446,18 @@ define([
|
|||
|
||||
showCondorcetWinner: function(answers, opts, uid, form) {
|
||||
|
||||
var showCondorcetWinner = {}
|
||||
|
||||
var _answers = parseAnswers(answers);
|
||||
|
||||
optionArray = []
|
||||
var optionArray = [];
|
||||
opts.values.forEach(function (option) {
|
||||
optionArray.push(option.v)
|
||||
})
|
||||
optionArray.push(option.v);
|
||||
});
|
||||
|
||||
listOfLists = []
|
||||
var listOfLists = [];
|
||||
Object.keys(_answers).forEach(function(a) {
|
||||
listOfLists.push(_answers[a].msg[uid])
|
||||
})
|
||||
return Condorcet.showCondorcetWinner(_answers, opts, uid, form)
|
||||
listOfLists.push(_answers[a].msg[uid]);
|
||||
});
|
||||
return Condorcet.showCondorcetWinner(_answers, opts, uid, form, optionArray, listOfLists);
|
||||
},
|
||||
|
||||
icon: h('i.cptools.cptools-form-list-ordered')
|
||||
|
@ -2808,48 +2817,50 @@ define([
|
|||
});
|
||||
|
||||
|
||||
let condorcetWinnerDiv = h('div')
|
||||
let condorcetWinnerDiv = h('div');
|
||||
|
||||
if (type == "sort") {
|
||||
condorcetWinnerDiv = h('div.cp-form-block', {style: { margin: 100, padding:100 }})
|
||||
if (form[uid].condorcet["method"] == 'schulze') {
|
||||
var condorcetResults = model.showCondorcetWinner(answers, block.opts, uid, form)[0][0]
|
||||
var condorcetWinner = condorcetResults[Object.keys(condorcetResults).length - 1]
|
||||
if (type === "sort") {
|
||||
condorcetWinnerDiv = h('div.cp-form-block', {style: { margin: 100, padding:100 }});
|
||||
var rankedResults = model.showCondorcetWinner(answers, block.opts, uid, form)[0][0];
|
||||
var condorcetWinner;
|
||||
if (form[uid].condorcet["method"] === 'schulze') {
|
||||
condorcetWinner = rankedResults[Object.keys(rankedResults).length - 1];
|
||||
condorcetWinner.forEach(function(option) {
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_showCondorcetWinner, option, {style: { margin: '10px'}}))
|
||||
})
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_showCondorcetWinner, option, {style: { margin: '10px'}}));
|
||||
});
|
||||
} else {
|
||||
var condorcetResults = model.showCondorcetWinner(answers, block.opts, uid, form)[0][0]
|
||||
var condorcetWinner = condorcetResults[0]
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_showCondorcetWinner, condorcetWinner, {style: { margin: '10px'}}))
|
||||
condorcetWinner = rankedResults[0];
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_showCondorcetWinner, condorcetWinner, {style: { margin: '10px'}}));
|
||||
}
|
||||
|
||||
|
||||
if (form[uid].condorcet["display"] == 'extended') {
|
||||
|
||||
if (form[uid].condorcet["method"] == 'schulze') {
|
||||
var results = Object.keys(rankedResults).reverse().map(function(result) {
|
||||
if (form[uid].condorcet["display"] === 'extended') {
|
||||
var sortedRankDict = model.showCondorcetWinner(answers, block.opts, uid, form)[0][1];
|
||||
var alternateWinner;
|
||||
var results;
|
||||
if (form[uid].condorcet["method"] === 'schulze') {
|
||||
results = Object.keys(rankedResults).reverse().map(function(result) {
|
||||
return rankedResults[result] + ' : ' + result;
|
||||
});
|
||||
var alternateWinner = model.showCondorcetWinner(answers, block.opts, uid, form)[1][0][0]
|
||||
alternateWinner = model.showCondorcetWinner(answers, block.opts, uid, form)[1][0][0];
|
||||
|
||||
} else {
|
||||
var results = Object.keys(sortedRankDict).map(function(result) {
|
||||
results = Object.keys(sortedRankDict).map(function(result) {
|
||||
return result + ' : ' + sortedRankDict[result];
|
||||
});
|
||||
var alternateWinner = model.showCondorcetWinner(answers, block.opts, uid, form)[1][0][model.showCondorcetWinner(answers, block.opts, uid, form)[0][0].length - 1]
|
||||
alternateWinner = model.showCondorcetWinner(answers, block.opts, uid, form)[1][0][model.showCondorcetWinner(answers, block.opts, uid, form)[0][0].length - 1];
|
||||
}
|
||||
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_condorcetExtendedDisplay, results.join(', '), {style: { margin: '10px'}}))
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_condorcetExtendedDisplay, results.join(', '), {style: { margin: '10px'}}));
|
||||
|
||||
var showAlternateWinnerButton = h('button.btn.btn-secondary', Messages.form_showAlternateWinnerButton, {style: { margin: '10px'}})
|
||||
condorcetWinnerDiv.append(h('div', showAlternateWinnerButton))
|
||||
var showAlternateWinnerButton = h('button.btn.btn-secondary', Messages.form_showAlternateWinnerButton, {style: { margin: '10px'}});
|
||||
condorcetWinnerDiv.append(h('div', showAlternateWinnerButton));
|
||||
|
||||
$(showAlternateWinnerButton).click(function() {
|
||||
if ($(condorcetWinnerDiv).children().length < 4) {
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_showAlternateWinner, alternateWinner, {style: { margin: '10px'}}))
|
||||
condorcetWinnerDiv.append(h('div', Messages.form_showAlternateWinner, alternateWinner, {style: { margin: '10px'}}));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -3734,38 +3745,38 @@ define([
|
|||
uid: uid,
|
||||
tmp: temp && temp[uid]
|
||||
});
|
||||
var chooseCondorcetDiv = h('div')
|
||||
if (type == "sort") {
|
||||
var schulzeButton = h('button.btn.btn-secondary', Messages.form_schulzeMethod)
|
||||
var rankedPairButton = h('button.btn.btn-secondary', Messages.form_rankedPairs)
|
||||
var chooseCondorcetDiv = h('div');
|
||||
if (type === "sort") {
|
||||
var schulzeButton = h('button.btn.btn-secondary', Messages.form_schulzeMethod);
|
||||
var rankedPairButton = h('button.btn.btn-secondary', Messages.form_rankedPairs);
|
||||
var chooseCondorcetMethod = h('div',
|
||||
Messages.form_chooseCondorcetMethod,
|
||||
schulzeButton, ' ',
|
||||
rankedPairButton,
|
||||
{style: { margin: '10px'}}
|
||||
)
|
||||
var basicDisplayButton = h('button.btn.btn-secondary', Messages.form_condorcetBasicDisplayButton)
|
||||
var extendedDisplayButton = h('button.btn.btn-secondary', Messages.form_condorcetExtendedDisplayButton,)
|
||||
);
|
||||
var basicDisplayButton = h('button.btn.btn-secondary', Messages.form_condorcetBasicDisplayButton);
|
||||
var extendedDisplayButton = h('button.btn.btn-secondary', Messages.form_condorcetExtendedDisplayButton);
|
||||
var chooseCondorcetDisplay = h('div',
|
||||
Messages.form_chooseCondorcetDisplay,
|
||||
basicDisplayButton, ' ',
|
||||
extendedDisplayButton,
|
||||
{style: { margin: '10px'}}
|
||||
)
|
||||
chooseCondorcetDiv.append(chooseCondorcetMethod, chooseCondorcetDisplay)
|
||||
form[uid].condorcet = {method: 'schulze', display: 'basic'}
|
||||
);
|
||||
chooseCondorcetDiv.append(chooseCondorcetMethod, chooseCondorcetDisplay);
|
||||
form[uid].condorcet = {method: 'schulze', display: 'basic'};
|
||||
$(schulzeButton).click(function() {
|
||||
form[uid].condorcet["method"] = 'schulze'
|
||||
})
|
||||
form[uid].condorcet["method"] = 'schulze';
|
||||
});
|
||||
$(rankedPairButton).click(function() {
|
||||
form[uid].condorcet["method"] = 'ranked'
|
||||
})
|
||||
form[uid].condorcet["method"] = 'ranked';
|
||||
});
|
||||
$(basicDisplayButton).click(function() {
|
||||
form[uid].condorcet["display"] = 'basic'
|
||||
})
|
||||
form[uid].condorcet["display"] = 'basic';
|
||||
});
|
||||
$(extendedDisplayButton).click(function() {
|
||||
form[uid].condorcet["display"] = 'extended'
|
||||
})
|
||||
form[uid].condorcet["display"] = 'extended';
|
||||
});
|
||||
}
|
||||
if (!data) { return; }
|
||||
data.uid = uid;
|
||||
|
|
Loading…
Reference in a new issue