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