Skip to content

Instantly share code, notes, and snippets.

@matyasfodor
Created March 22, 2018 08:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matyasfodor/c7569ca69bde4d54912742db5815e6a5 to your computer and use it in GitHub Desktop.
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.
// ==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