Created
March 22, 2018 08:10
-
-
Save matyasfodor/c7569ca69bde4d54912742db5815e6a5 to your computer and use it in GitHub Desktop.
Tampermonkey script that sorts SO answers in descending order by vote count. The accepted answer is the first one.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Sort Answers | |
// @namespace TBD | |
// @version 1.0.0.1 | |
// @description sorts SO answers in descending order by vote count. The accepted answer is the first one. | |
// @author matyasfodor | |
// @include /https?:\/\/(meta\.)?stackoverflow\.com/questions/.*/ | |
// @grant none | |
// ==/UserScript== | |
/* jshint -W097 */ | |
'use strict'; | |
// Utilities | |
/** | |
* maybe | |
* | |
* Executed `cb` with `val` if `val` is existy (not null and not undefined). | |
* Falls back to defVal if provided | |
* Returns `undefined` otherwise | |
* not that existy is not equal to falsy, since zero is existy, but is falsy | |
*/ | |
const maybe = (value, cb, defVal = null) => | |
(typeof value !== 'undefined' && value !== null) | |
? cb(value) | |
: (defVal !== null) | |
? cb(defVal) | |
: undefined; | |
/** | |
* _zip | |
* | |
* Utility for zipShort | |
*/ | |
const _zip = (func, args) => { | |
const iterators = args.map(arr => arr[Symbol.iterator]()); | |
let iterateInstances = iterators.map((i) => i.next()); | |
const ret = []; | |
while(iterateInstances[func](it => !it.done)) { | |
ret.push(iterateInstances.map(it => it.value)); | |
iterateInstances = iterators.map((i) => i.next()); | |
} | |
return ret; | |
}; | |
/** | |
* zipShort | |
* | |
* takes arbitrary number of arrays and transposes them | |
* zipShort([1, 4], [2, 5], [3, 6]) === [[1, 2, 3], [4, 5, 6]] | |
*/ | |
const zipShort = (...args) => _zip('every', args); | |
/////////////////// | |
// SO answer sorter | |
(function() { | |
// Container of aanswers - not it has multiple type of children but we're only interested in `.answer`s. | |
const answersElement = document.getElementById('answers'); | |
if (answersElement === null) return; | |
// Answers: {elelent: DOMelement, index:nth child of answersElement} | |
const answers = Array.from(answersElement.children) | |
.map((element, nthChildIndex) => ({element, nthChildIndex})) | |
.filter(({element}) => element.classList.contains('answer')); | |
// sort answers by vote count (descending) | |
let sortedAnswers = answers.map(answer => ({ | |
...answer, | |
vote: parseFloat(maybe(answer.element.getElementsByClassName('vote-count-post')[0], element => element.innerHTML) | '0') | |
})).sort((a, b) => b.vote - a.vote); | |
// Find accepted answer | |
const acceptedAnswerIndex = sortedAnswers.findIndex(({element}) => element.classList.contains('accepted-answer')); | |
// If it's found, then put it in the first place | |
if (0 < acceptedAnswerIndex) { | |
const [acceptedAnswer] = sortedAnswers.splice(acceptedAnswerIndex, 1); | |
sortedAnswers = [acceptedAnswer, ...sortedAnswers]; | |
} | |
// Get the nth child positions and sort them (ascending) | |
const positions = sortedAnswers.map(({nthChildIndex}) => nthChildIndex).sort((a, b) => a - b); | |
// Replace the answers at given positions with the sorted answers | |
zipShort(positions, sortedAnswers).forEach(([position, {element}]) => { | |
answersElement.replaceChild(element, answersElement.children[position]); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment