compareNatural.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.createCompareNatural = void 0;
  7. var _javascriptNaturalSort = _interopRequireDefault(require("javascript-natural-sort"));
  8. var _is = require("../../utils/is.js");
  9. var _factory = require("../../utils/factory.js");
  10. var name = 'compareNatural';
  11. var dependencies = ['typed', 'compare'];
  12. var createCompareNatural = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
  13. var typed = _ref.typed,
  14. compare = _ref.compare;
  15. var compareBooleans = compare.signatures['boolean,boolean'];
  16. /**
  17. * Compare two values of any type in a deterministic, natural way.
  18. *
  19. * For numeric values, the function works the same as `math.compare`.
  20. * For types of values that can't be compared mathematically,
  21. * the function compares in a natural way.
  22. *
  23. * For numeric values, x and y are considered equal when the relative
  24. * difference between x and y is smaller than the configured epsilon.
  25. * The function cannot be used to compare values smaller than
  26. * approximately 2.22e-16.
  27. *
  28. * For Complex numbers, first the real parts are compared. If equal,
  29. * the imaginary parts are compared.
  30. *
  31. * Strings are compared with a natural sorting algorithm, which
  32. * orders strings in a "logic" way following some heuristics.
  33. * This differs from the function `compare`, which converts the string
  34. * into a numeric value and compares that. The function `compareText`
  35. * on the other hand compares text lexically.
  36. *
  37. * Arrays and Matrices are compared value by value until there is an
  38. * unequal pair of values encountered. Objects are compared by sorted
  39. * keys until the keys or their values are unequal.
  40. *
  41. * Syntax:
  42. *
  43. * math.compareNatural(x, y)
  44. *
  45. * Examples:
  46. *
  47. * math.compareNatural(6, 1) // returns 1
  48. * math.compareNatural(2, 3) // returns -1
  49. * math.compareNatural(7, 7) // returns 0
  50. *
  51. * math.compareNatural('10', '2') // returns 1
  52. * math.compareText('10', '2') // returns -1
  53. * math.compare('10', '2') // returns 1
  54. *
  55. * math.compareNatural('Answer: 10', 'Answer: 2') // returns 1
  56. * math.compareText('Answer: 10', 'Answer: 2') // returns -1
  57. * math.compare('Answer: 10', 'Answer: 2')
  58. * // Error: Cannot convert "Answer: 10" to a number
  59. *
  60. * const a = math.unit('5 cm')
  61. * const b = math.unit('40 mm')
  62. * math.compareNatural(a, b) // returns 1
  63. *
  64. * const c = math.complex('2 + 3i')
  65. * const d = math.complex('2 + 4i')
  66. * math.compareNatural(c, d) // returns -1
  67. *
  68. * math.compareNatural([1, 2, 4], [1, 2, 3]) // returns 1
  69. * math.compareNatural([1, 2, 3], [1, 2]) // returns 1
  70. * math.compareNatural([1, 5], [1, 2, 3]) // returns 1
  71. * math.compareNatural([1, 2], [1, 2]) // returns 0
  72. *
  73. * math.compareNatural({a: 2}, {a: 4}) // returns -1
  74. *
  75. * See also:
  76. *
  77. * compare, compareText
  78. *
  79. * @param {*} x First value to compare
  80. * @param {*} y Second value to compare
  81. * @return {number} Returns the result of the comparison:
  82. * 1 when x > y, -1 when x < y, and 0 when x == y.
  83. */
  84. return typed(name, {
  85. 'any, any': _compareNatural
  86. }); // just to check # args
  87. function _compareNatural(x, y) {
  88. var typeX = (0, _is.typeOf)(x);
  89. var typeY = (0, _is.typeOf)(y);
  90. var c;
  91. // numeric types
  92. if ((typeX === 'number' || typeX === 'BigNumber' || typeX === 'Fraction') && (typeY === 'number' || typeY === 'BigNumber' || typeY === 'Fraction')) {
  93. c = compare(x, y);
  94. if (c.toString() !== '0') {
  95. // c can be number, BigNumber, or Fraction
  96. return c > 0 ? 1 : -1; // return a number
  97. } else {
  98. return (0, _javascriptNaturalSort["default"])(typeX, typeY);
  99. }
  100. }
  101. // matrix types
  102. var matTypes = ['Array', 'DenseMatrix', 'SparseMatrix'];
  103. if (matTypes.includes(typeX) || matTypes.includes(typeY)) {
  104. c = compareMatricesAndArrays(_compareNatural, x, y);
  105. if (c !== 0) {
  106. return c;
  107. } else {
  108. return (0, _javascriptNaturalSort["default"])(typeX, typeY);
  109. }
  110. }
  111. // in case of different types, order by name of type, i.e. 'BigNumber' < 'Complex'
  112. if (typeX !== typeY) {
  113. return (0, _javascriptNaturalSort["default"])(typeX, typeY);
  114. }
  115. if (typeX === 'Complex') {
  116. return compareComplexNumbers(x, y);
  117. }
  118. if (typeX === 'Unit') {
  119. if (x.equalBase(y)) {
  120. return _compareNatural(x.value, y.value);
  121. }
  122. // compare by units
  123. return compareArrays(_compareNatural, x.formatUnits(), y.formatUnits());
  124. }
  125. if (typeX === 'boolean') {
  126. return compareBooleans(x, y);
  127. }
  128. if (typeX === 'string') {
  129. return (0, _javascriptNaturalSort["default"])(x, y);
  130. }
  131. if (typeX === 'Object') {
  132. return compareObjects(_compareNatural, x, y);
  133. }
  134. if (typeX === 'null') {
  135. return 0;
  136. }
  137. if (typeX === 'undefined') {
  138. return 0;
  139. }
  140. // this should not occur...
  141. throw new TypeError('Unsupported type of value "' + typeX + '"');
  142. }
  143. /**
  144. * Compare mixed matrix/array types, by converting to same-shaped array.
  145. * This comparator is non-deterministic regarding input types.
  146. * @param {Array | SparseMatrix | DenseMatrix | *} x
  147. * @param {Array | SparseMatrix | DenseMatrix | *} y
  148. * @returns {number} Returns the comparison result: -1, 0, or 1
  149. */
  150. function compareMatricesAndArrays(compareNatural, x, y) {
  151. if ((0, _is.isSparseMatrix)(x) && (0, _is.isSparseMatrix)(y)) {
  152. return compareArrays(compareNatural, x.toJSON().values, y.toJSON().values);
  153. }
  154. if ((0, _is.isSparseMatrix)(x)) {
  155. // note: convert to array is expensive
  156. return compareMatricesAndArrays(compareNatural, x.toArray(), y);
  157. }
  158. if ((0, _is.isSparseMatrix)(y)) {
  159. // note: convert to array is expensive
  160. return compareMatricesAndArrays(compareNatural, x, y.toArray());
  161. }
  162. // convert DenseArray into Array
  163. if ((0, _is.isDenseMatrix)(x)) {
  164. return compareMatricesAndArrays(compareNatural, x.toJSON().data, y);
  165. }
  166. if ((0, _is.isDenseMatrix)(y)) {
  167. return compareMatricesAndArrays(compareNatural, x, y.toJSON().data);
  168. }
  169. // convert scalars to array
  170. if (!Array.isArray(x)) {
  171. return compareMatricesAndArrays(compareNatural, [x], y);
  172. }
  173. if (!Array.isArray(y)) {
  174. return compareMatricesAndArrays(compareNatural, x, [y]);
  175. }
  176. return compareArrays(compareNatural, x, y);
  177. }
  178. /**
  179. * Compare two Arrays
  180. *
  181. * - First, compares value by value
  182. * - Next, if all corresponding values are equal,
  183. * look at the length: longest array will be considered largest
  184. *
  185. * @param {Array} x
  186. * @param {Array} y
  187. * @returns {number} Returns the comparison result: -1, 0, or 1
  188. */
  189. function compareArrays(compareNatural, x, y) {
  190. // compare each value
  191. for (var i = 0, ii = Math.min(x.length, y.length); i < ii; i++) {
  192. var v = compareNatural(x[i], y[i]);
  193. if (v !== 0) {
  194. return v;
  195. }
  196. }
  197. // compare the size of the arrays
  198. if (x.length > y.length) {
  199. return 1;
  200. }
  201. if (x.length < y.length) {
  202. return -1;
  203. }
  204. // both Arrays have equal size and content
  205. return 0;
  206. }
  207. /**
  208. * Compare two objects
  209. *
  210. * - First, compare sorted property names
  211. * - Next, compare the property values
  212. *
  213. * @param {Object} x
  214. * @param {Object} y
  215. * @returns {number} Returns the comparison result: -1, 0, or 1
  216. */
  217. function compareObjects(compareNatural, x, y) {
  218. var keysX = Object.keys(x);
  219. var keysY = Object.keys(y);
  220. // compare keys
  221. keysX.sort(_javascriptNaturalSort["default"]);
  222. keysY.sort(_javascriptNaturalSort["default"]);
  223. var c = compareArrays(compareNatural, keysX, keysY);
  224. if (c !== 0) {
  225. return c;
  226. }
  227. // compare values
  228. for (var i = 0; i < keysX.length; i++) {
  229. var v = compareNatural(x[keysX[i]], y[keysY[i]]);
  230. if (v !== 0) {
  231. return v;
  232. }
  233. }
  234. return 0;
  235. }
  236. });
  237. /**
  238. * Compare two complex numbers, `x` and `y`:
  239. *
  240. * - First, compare the real values of `x` and `y`
  241. * - If equal, compare the imaginary values of `x` and `y`
  242. *
  243. * @params {Complex} x
  244. * @params {Complex} y
  245. * @returns {number} Returns the comparison result: -1, 0, or 1
  246. */
  247. exports.createCompareNatural = createCompareNatural;
  248. function compareComplexNumbers(x, y) {
  249. if (x.re > y.re) {
  250. return 1;
  251. }
  252. if (x.re < y.re) {
  253. return -1;
  254. }
  255. if (x.im > y.im) {
  256. return 1;
  257. }
  258. if (x.im < y.im) {
  259. return -1;
  260. }
  261. return 0;
  262. }