|
|
|
/**
|
|
|
|
* Fisher-Yates shuffle.
|
|
|
|
* http://stackoverflow.com/a/962890/373422
|
|
|
|
* https://bost.ocks.org/mike/shuffle/
|
|
|
|
* @param {Array} array Array to shuffle.
|
|
|
|
* @return {Array} Randomly sorted array.
|
|
|
|
*/
|
|
|
|
function randomize(array) {
|
|
|
|
let n = array.length;
|
|
|
|
|
|
|
|
while (n) {
|
|
|
|
n -= 1;
|
|
|
|
const i = Math.floor(Math.random() * (n + 1));
|
|
|
|
const temp = array[i];
|
|
|
|
array[i] = array[n];
|
|
|
|
array[n] = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
const defaults = {
|
|
|
|
// Use array.reverse() to reverse the results
|
|
|
|
reverse: false,
|
|
|
|
|
|
|
|
// Sorting function
|
|
|
|
by: null,
|
|
|
|
|
|
|
|
// Custom sort function
|
|
|
|
compare: null,
|
|
|
|
|
|
|
|
// If true, this will skip the sorting and return a randomized order in the array
|
|
|
|
randomize: false,
|
|
|
|
|
|
|
|
// Determines which property of each item in the array is passed to the
|
|
|
|
// sorting method.
|
|
|
|
key: 'element',
|
|
|
|
};
|
|
|
|
|
|
|
|
// You can return `undefined` from the `by` function to revert to DOM order.
|
|
|
|
export default function sorter(arr, options) {
|
|
|
|
const opts = Object.assign({}, defaults, options);
|
|
|
|
const original = Array.from(arr);
|
|
|
|
let revert = false;
|
|
|
|
|
|
|
|
if (!arr.length) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.randomize) {
|
|
|
|
return randomize(arr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort the elements by the opts.by function.
|
|
|
|
// If we don't have opts.by, default to DOM order
|
|
|
|
if (typeof opts.by === 'function') {
|
|
|
|
arr.sort((a, b) => {
|
|
|
|
// Exit early if we already know we want to revert
|
|
|
|
if (revert) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const valA = opts.by(a[opts.key]);
|
|
|
|
const valB = opts.by(b[opts.key]);
|
|
|
|
|
|
|
|
// If both values are undefined, use the DOM order
|
|
|
|
if (valA === undefined && valB === undefined) {
|
|
|
|
revert = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (valA < valB || valA === 'sortFirst' || valB === 'sortLast') {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (valA > valB || valA === 'sortLast' || valB === 'sortFirst') {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
} else if (typeof opts.compare === 'function') {
|
|
|
|
arr.sort(opts.compare);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Revert to the original array if necessary
|
|
|
|
if (revert) {
|
|
|
|
return original;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.reverse) {
|
|
|
|
arr.reverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr;
|
|
|
|
}
|