Comparator

Configurable Comparator similar to java.util.Comparator in Java 8

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/390752/745016/Comparator.js

  1. // ==UserScript==
  2. // @name Comparator
  3. // @namespace hoehleg.userscripts.private
  4. // @version 0.4
  5. // @description Configurable Comparator similar to java.util.Comparator in Java 8
  6. // @author Gerrit Höhle
  7. // @grant none
  8. // ==/UserScript==
  9.  
  10. /* jshint esnext: true */
  11. const Comparator = (() => {
  12. 'use strict';
  13.  
  14. // internal functions
  15. const isMissing = obj => obj === null || typeof obj === "undefined";
  16. const compareValues = (a, b) => (a < b) ? -1 : ((a === b) ? 0 : 1);
  17. const compareByMissing = (a, b) => isMissing(a) ? (isMissing(b) ? 0 : 1) : (isMissing(b) ? -1 : null);
  18.  
  19. /**
  20. * Basic compare function
  21. * @param {*} a - Object or value to compare
  22. * @param {*} b - Object or value to which to compare
  23. * @param {boolean} [missingFirst=false] - order of "undefined" and "null". true = first, false = last.
  24. * @returns "-1" for a < b, "0" for a === b or "1" for a > b
  25. **/
  26. const compare = (a, b, missingFirst = false) => {
  27. let result = missingFirst ? compareByMissing(b, a) : compareByMissing(a, b);
  28. if (result !== null) {
  29. return result;
  30. }
  31.  
  32. if (typeof a === 'string' || a instanceof String) {
  33. return a.localeCompare(b);
  34. }
  35.  
  36. if (a instanceof Date) {
  37. return compareValues(a.valueOf(), b.valueOf());
  38. }
  39.  
  40. return compareValues(a, b);
  41. };
  42.  
  43. /**
  44. * Creates the compare-function bound to a Comparator.
  45. * @param {Comparator} cmparator - the Comparator the compare - function should belong to
  46. * @returns {Function} the compare(a, b) - function
  47. */
  48. const createCompareFunction = (comparator) => {
  49. /**
  50. * Compare function bound to a comparator.
  51. * When executed the current setup of the comparator is considered
  52. * @param {*} a - Object or value to compare
  53. * @param {*} b - Object or value to which to compare
  54. **/
  55. return (a, b) => {
  56. if (comparator.isAscending) {
  57. const tmp = a;
  58. a = b;
  59. b = tmp;
  60. }
  61.  
  62. let result = comparator.isMissingFirst ? compareByMissing(b, a) : compareByMissing(a, b);
  63.  
  64. for (let i = 0; !result && i < comparator.sortByFunctionsAndComparators.length; i++) {
  65. const accessorFnc = comparator.sortByFunctionsAndComparators[i];
  66.  
  67. if (accessorFnc instanceof Comparator) {
  68. result = accessorFnc.compare(a, b);
  69. } else {
  70. result = compare(accessorFnc(a), accessorFnc(b), comparator.isMissingFirst);
  71. }
  72. }
  73.  
  74. return result !== null ? result : compare(a, b, comparator.isMissingFirst);
  75. };
  76. };
  77.  
  78. // private field accessor
  79. const _isReverse = Symbol("isReverse");
  80. const _accessorFunctions = Symbol("accessorFunctions");
  81. const _isMissingFirst = Symbol("isMissingFirst");
  82. const _compareFunction = Symbol("compareFunction");
  83.  
  84. // export class
  85. return class Comparator {
  86. /**
  87. * @param {Array.<function|Compator>} sortByFunctionsAndComparators - accessor-functions or comparators to evaluate values to sort by
  88. */
  89. constructor(...sortByFunctionsAndComparators) {
  90. this[_accessorFunctions] = sortByFunctionsAndComparators;
  91. this[_isReverse] = false;
  92. this[_isMissingFirst] = false;
  93. this[_compareFunction] = createCompareFunction(this);
  94. }
  95.  
  96. /**
  97. * @type {Array.<function|Compator>} - accessor-functions and/or comparators to evaluate values to sort by
  98. **/
  99. get sortByFunctionsAndComparators() {
  100. return [...this[_accessorFunctions]];
  101. }
  102.  
  103. get isAscending() {
  104. return this[_isReverse];
  105. }
  106.  
  107. get isDescending() {
  108. return !this.isAscending;
  109. }
  110.  
  111. get isMissingFirst() {
  112. return this[_isMissingFirst];
  113. }
  114.  
  115. get isMissingLast() {
  116. return !this.isMissingFirst;
  117. }
  118.  
  119. /**
  120. * @type {Function} - the compare(a, b) - function that returns "-1" for a < b, "0" for a === b or "1" for a > b.
  121. * the funtion is setup by this Comparator
  122. **/
  123. get compareFnc() {
  124. return this[_compareFunction];
  125. }
  126.  
  127. /**
  128. * Defines that "undefined" and "null" gets sorted to the begin
  129. **/
  130. setMissingFirst() {
  131. this[_isMissingFirst] = true;
  132. return this;
  133. }
  134.  
  135. /**
  136. * Defines that "undefined" and "null" gets sorted to the end
  137. **/
  138. setMissingLast() {
  139. this[_isMissingFirst] = false;
  140. return this;
  141. }
  142.  
  143. /**
  144. * Reverses the sort order from ascending to descending and vice versa
  145. **/
  146. reverse() {
  147. this[_isReverse] = !this[_isReverse];
  148. return this;
  149. }
  150.  
  151. /**
  152. * Compares an object or value to another considering the setup of this Comparator
  153. * @param {*} a - Object or value to compare
  154. * @param {*} b - Object or value to compare by
  155. * @returns "-1" for a < b, "0" for a === b or "1" for a > b
  156. **/
  157. compare(a, b) {
  158. return this.compareFnc(a, b);
  159. }
  160. /**
  161. * Compares an object or value to another one
  162. * @param {*} a - Object or value to compare
  163. * @param {*} b - Object or value to compare by
  164. * @param {boolean} [missingFirst=false] - order of "undefined" and "null". true = first, false = last.
  165. * @returns "-1" for a < b, "0" for a === b or "1" for a > b
  166. **/
  167. static compare(a, b, missingFirst = false) {
  168. return compare(a, b, missingFirst);
  169. }
  170. };
  171. })();