123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- import naturalSort from 'javascript-natural-sort';
- import { isDenseMatrix, isSparseMatrix, typeOf } from '../../utils/is.js';
- import { factory } from '../../utils/factory.js';
- var name = 'compareNatural';
- var dependencies = ['typed', 'compare'];
- export var createCompareNatural = /* #__PURE__ */factory(name, dependencies, _ref => {
- var {
- typed,
- compare
- } = _ref;
- var compareBooleans = compare.signatures['boolean,boolean'];
- /**
- * Compare two values of any type in a deterministic, natural way.
- *
- * For numeric values, the function works the same as `math.compare`.
- * For types of values that can't be compared mathematically,
- * the function compares in a natural way.
- *
- * For numeric values, x and y are considered equal when the relative
- * difference between x and y is smaller than the configured epsilon.
- * The function cannot be used to compare values smaller than
- * approximately 2.22e-16.
- *
- * For Complex numbers, first the real parts are compared. If equal,
- * the imaginary parts are compared.
- *
- * Strings are compared with a natural sorting algorithm, which
- * orders strings in a "logic" way following some heuristics.
- * This differs from the function `compare`, which converts the string
- * into a numeric value and compares that. The function `compareText`
- * on the other hand compares text lexically.
- *
- * Arrays and Matrices are compared value by value until there is an
- * unequal pair of values encountered. Objects are compared by sorted
- * keys until the keys or their values are unequal.
- *
- * Syntax:
- *
- * math.compareNatural(x, y)
- *
- * Examples:
- *
- * math.compareNatural(6, 1) // returns 1
- * math.compareNatural(2, 3) // returns -1
- * math.compareNatural(7, 7) // returns 0
- *
- * math.compareNatural('10', '2') // returns 1
- * math.compareText('10', '2') // returns -1
- * math.compare('10', '2') // returns 1
- *
- * math.compareNatural('Answer: 10', 'Answer: 2') // returns 1
- * math.compareText('Answer: 10', 'Answer: 2') // returns -1
- * math.compare('Answer: 10', 'Answer: 2')
- * // Error: Cannot convert "Answer: 10" to a number
- *
- * const a = math.unit('5 cm')
- * const b = math.unit('40 mm')
- * math.compareNatural(a, b) // returns 1
- *
- * const c = math.complex('2 + 3i')
- * const d = math.complex('2 + 4i')
- * math.compareNatural(c, d) // returns -1
- *
- * math.compareNatural([1, 2, 4], [1, 2, 3]) // returns 1
- * math.compareNatural([1, 2, 3], [1, 2]) // returns 1
- * math.compareNatural([1, 5], [1, 2, 3]) // returns 1
- * math.compareNatural([1, 2], [1, 2]) // returns 0
- *
- * math.compareNatural({a: 2}, {a: 4}) // returns -1
- *
- * See also:
- *
- * compare, compareText
- *
- * @param {*} x First value to compare
- * @param {*} y Second value to compare
- * @return {number} Returns the result of the comparison:
- * 1 when x > y, -1 when x < y, and 0 when x == y.
- */
- return typed(name, {
- 'any, any': _compareNatural
- }); // just to check # args
- function _compareNatural(x, y) {
- var typeX = typeOf(x);
- var typeY = typeOf(y);
- var c;
- // numeric types
- if ((typeX === 'number' || typeX === 'BigNumber' || typeX === 'Fraction') && (typeY === 'number' || typeY === 'BigNumber' || typeY === 'Fraction')) {
- c = compare(x, y);
- if (c.toString() !== '0') {
- // c can be number, BigNumber, or Fraction
- return c > 0 ? 1 : -1; // return a number
- } else {
- return naturalSort(typeX, typeY);
- }
- }
- // matrix types
- var matTypes = ['Array', 'DenseMatrix', 'SparseMatrix'];
- if (matTypes.includes(typeX) || matTypes.includes(typeY)) {
- c = compareMatricesAndArrays(_compareNatural, x, y);
- if (c !== 0) {
- return c;
- } else {
- return naturalSort(typeX, typeY);
- }
- }
- // in case of different types, order by name of type, i.e. 'BigNumber' < 'Complex'
- if (typeX !== typeY) {
- return naturalSort(typeX, typeY);
- }
- if (typeX === 'Complex') {
- return compareComplexNumbers(x, y);
- }
- if (typeX === 'Unit') {
- if (x.equalBase(y)) {
- return _compareNatural(x.value, y.value);
- }
- // compare by units
- return compareArrays(_compareNatural, x.formatUnits(), y.formatUnits());
- }
- if (typeX === 'boolean') {
- return compareBooleans(x, y);
- }
- if (typeX === 'string') {
- return naturalSort(x, y);
- }
- if (typeX === 'Object') {
- return compareObjects(_compareNatural, x, y);
- }
- if (typeX === 'null') {
- return 0;
- }
- if (typeX === 'undefined') {
- return 0;
- }
- // this should not occur...
- throw new TypeError('Unsupported type of value "' + typeX + '"');
- }
- /**
- * Compare mixed matrix/array types, by converting to same-shaped array.
- * This comparator is non-deterministic regarding input types.
- * @param {Array | SparseMatrix | DenseMatrix | *} x
- * @param {Array | SparseMatrix | DenseMatrix | *} y
- * @returns {number} Returns the comparison result: -1, 0, or 1
- */
- function compareMatricesAndArrays(compareNatural, x, y) {
- if (isSparseMatrix(x) && isSparseMatrix(y)) {
- return compareArrays(compareNatural, x.toJSON().values, y.toJSON().values);
- }
- if (isSparseMatrix(x)) {
- // note: convert to array is expensive
- return compareMatricesAndArrays(compareNatural, x.toArray(), y);
- }
- if (isSparseMatrix(y)) {
- // note: convert to array is expensive
- return compareMatricesAndArrays(compareNatural, x, y.toArray());
- }
- // convert DenseArray into Array
- if (isDenseMatrix(x)) {
- return compareMatricesAndArrays(compareNatural, x.toJSON().data, y);
- }
- if (isDenseMatrix(y)) {
- return compareMatricesAndArrays(compareNatural, x, y.toJSON().data);
- }
- // convert scalars to array
- if (!Array.isArray(x)) {
- return compareMatricesAndArrays(compareNatural, [x], y);
- }
- if (!Array.isArray(y)) {
- return compareMatricesAndArrays(compareNatural, x, [y]);
- }
- return compareArrays(compareNatural, x, y);
- }
- /**
- * Compare two Arrays
- *
- * - First, compares value by value
- * - Next, if all corresponding values are equal,
- * look at the length: longest array will be considered largest
- *
- * @param {Array} x
- * @param {Array} y
- * @returns {number} Returns the comparison result: -1, 0, or 1
- */
- function compareArrays(compareNatural, x, y) {
- // compare each value
- for (var i = 0, ii = Math.min(x.length, y.length); i < ii; i++) {
- var v = compareNatural(x[i], y[i]);
- if (v !== 0) {
- return v;
- }
- }
- // compare the size of the arrays
- if (x.length > y.length) {
- return 1;
- }
- if (x.length < y.length) {
- return -1;
- }
- // both Arrays have equal size and content
- return 0;
- }
- /**
- * Compare two objects
- *
- * - First, compare sorted property names
- * - Next, compare the property values
- *
- * @param {Object} x
- * @param {Object} y
- * @returns {number} Returns the comparison result: -1, 0, or 1
- */
- function compareObjects(compareNatural, x, y) {
- var keysX = Object.keys(x);
- var keysY = Object.keys(y);
- // compare keys
- keysX.sort(naturalSort);
- keysY.sort(naturalSort);
- var c = compareArrays(compareNatural, keysX, keysY);
- if (c !== 0) {
- return c;
- }
- // compare values
- for (var i = 0; i < keysX.length; i++) {
- var v = compareNatural(x[keysX[i]], y[keysY[i]]);
- if (v !== 0) {
- return v;
- }
- }
- return 0;
- }
- });
- /**
- * Compare two complex numbers, `x` and `y`:
- *
- * - First, compare the real values of `x` and `y`
- * - If equal, compare the imaginary values of `x` and `y`
- *
- * @params {Complex} x
- * @params {Complex} y
- * @returns {number} Returns the comparison result: -1, 0, or 1
- */
- function compareComplexNumbers(x, y) {
- if (x.re > y.re) {
- return 1;
- }
- if (x.re < y.re) {
- return -1;
- }
- if (x.im > y.im) {
- return 1;
- }
- if (x.im < y.im) {
- return -1;
- }
- return 0;
- }
|