UNPKG

@rimbu/common

Version:

Common types and objects used in many other Rimbu packages

542 lines 19.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Comp = void 0; var tslib_1 = require("tslib"); var internal_cjs_1 = require("./internal.cjs"); var Comp; (function (Comp) { var _anyFlatComp = createAnyComp('FLAT'); var _anyShallowComp = createAnyComp('SHALLOW'); var _anyDeepComp = createAnyComp('DEEP'); /** * Returns the default Comp instance, which is the Comp.anyDeepComp() instance. */ function defaultComp() { return _anyDeepComp; } Comp.defaultComp = defaultComp; var _numberComp = { isComparable: function (obj) { return typeof obj === 'number'; }, compare: function (v1, v2) { if (Number.isFinite(v1) && Number.isFinite(v2)) { return v1 - v2; } if (Number.isNaN(v1)) { if (Number.isNaN(v2)) return 0; if (v2 === Number.POSITIVE_INFINITY) return 1; if (v2 === Number.NEGATIVE_INFINITY) return -1; return -1; } // only infinities remain if (v1 === Number.POSITIVE_INFINITY) { return v2 === Number.POSITIVE_INFINITY ? 0 : 1; } // v1 === Number.NEGATIVE_INFINITY return v2 === Number.NEGATIVE_INFINITY ? 0 : -1; }, }; /** * Returns a default number Comp instance that orders numbers naturally. * @example * ```ts * const c = Comp.numberComp(); * console.log(c.compare(3, 5)) * // => -2 * ``` */ function numberComp() { return _numberComp; } Comp.numberComp = numberComp; var _booleanComp = { isComparable: function (obj) { return typeof obj === 'boolean'; }, compare: function (v1, v2) { return v1 === v2 ? 0 : v1 ? 1 : -1; }, }; /** * Returns a default boolean Comp instance that orders booleans according to false < true. * @example * ```ts * const c = Comp.booleanComp(); * console.log(c.compare(false, true) < 0) * // => true * console.log(c.compare(true, true)) * // => 0 * ``` */ function booleanComp() { return _booleanComp; } Comp.booleanComp = booleanComp; var _bigIntComp = { isComparable: function (obj) { return typeof obj === 'bigint'; }, compare: function (v1, v2) { var res = v1 - v2; if (res > 0) return 1; if (res < 0) return -1; return 0; }, }; /** * Returns a default bigint Comp instance that orders bigint numbers naturally. */ function bigIntComp() { return _bigIntComp; } Comp.bigIntComp = bigIntComp; var _defaultCollator = Intl.Collator('und'); var _stringComp = { isComparable: function (obj) { return typeof obj === 'string'; }, compare: _defaultCollator.compare, }; var _anyStringJSONComp = { isComparable: function (obj) { return true; }, compare: function (v1, v2) { return _defaultCollator.compare(JSON.stringify(v1), JSON.stringify(v2)); }, }; /** * Returns a Comp instance converts values to string with JSON.stringify, and orders the resulting string naturally. */ function anyStringJSONComp() { return _anyStringJSONComp; } Comp.anyStringJSONComp = anyStringJSONComp; /** * Returns a `Comp` instance that compares strings based on the string's `localeCompare` method. * @param locales - (optional) a locale or list of locales * @param options - (optional) see String.localeCompare for details */ function stringComp() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 0) return _stringComp; var collator = Intl.Collator.apply(Intl, tslib_1.__spreadArray([], tslib_1.__read(args), false)); return { isComparable: function (obj) { return typeof obj === 'string'; }, compare: collator.compare, }; } Comp.stringComp = stringComp; /** * Returns a `Comp` instance that compares strings in a case-insensitive way. */ function stringCaseInsensitiveComp() { return stringComp('und', { sensitivity: 'accent' }); } Comp.stringCaseInsensitiveComp = stringCaseInsensitiveComp; var _stringCharCodeComp = { isComparable: function (obj) { return typeof obj === 'string'; }, compare: function (v1, v2) { var len = Math.min(v1.length, v2.length); var i = -1; while (++i < len) { var diff = v1.charCodeAt(i) - v2.charCodeAt(i); if (diff !== 0) return diff; } return v1.length - v2.length; }, }; /** * Returns a string Comp instance that orders strings according to their indexed char codes. */ function stringCharCodeComp() { return _stringCharCodeComp; } Comp.stringCharCodeComp = stringCharCodeComp; var _anyToStringComp = { isComparable: function (obj) { return true; }, compare: function (v1, v2) { return _defaultCollator.compare(internal_cjs_1.Eq.convertAnyToString(v1), internal_cjs_1.Eq.convertAnyToString(v2)); }, }; /** * Returns a any Comp instance that orders any according to their toString values. */ function anyToStringComp() { return _anyToStringComp; } Comp.anyToStringComp = anyToStringComp; /** * Returns a Comp instance that orders objects with a `valueOf` method according to the given `valueComp` instance for the valueOf values. * @param cls - the constructor of the values the Comp instance can compare * @param valueComp - (optional) the Comp instance to use on the .valueOf values */ function createValueOfComp(cls, valueComp) { if (valueComp === void 0) { valueComp = anyShallowComp(); } return { isComparable: function (obj) { return obj instanceof cls; }, compare: function (v1, v2) { return valueComp.compare(v1.valueOf(), v2.valueOf()); }, }; } Comp.createValueOfComp = createValueOfComp; var _DateComp = createValueOfComp(Date, _numberComp); /** * Returns a Date Comp instance that orders Dates according to their `.valueOf` value. */ function dateComp() { return _DateComp; } Comp.dateComp = dateComp; function createIterableComp(itemComp) { return { isComparable: function (obj) { // unfortunately we cannot check element compatability return (typeof obj === 'object' && obj !== null && Symbol.iterator in obj); }, compare: function (v1, v2) { var iter1 = v1[Symbol.iterator](); var iter2 = v2[Symbol.iterator](); while (true) { var value1 = iter1.next(); var value2 = iter2.next(); if (value1.done) return value2.done ? 0 : -1; if (value2.done) return 1; var result = itemComp.compare(value1.value, value2.value); if (result !== 0) return result; } }, }; } var _iterableAnyComp = createIterableComp(Comp.defaultComp()); /** * Returns a Comp instance for Iterable objects that orders the Iterables by comparing the elements with the given `itemComp` Comp instance. * @param itemComp - (optional) the Comp instance to use to compare the Iterable's elements. * @example * ```ts * const c = Comp.iterableComp(); * console.log(c.compare([1, 3, 2], [1, 3, 2])) * // => 0 * console.log(c.compare([1, 2, 3, 4], [1, 3, 2]) < 0) * // => true * ``` */ function iterableComp(itemComp) { if (undefined === itemComp) return _iterableAnyComp; return createIterableComp(itemComp); } Comp.iterableComp = iterableComp; var _BooleanComp = createValueOfComp(Boolean, _booleanComp); var _NumberComp = createValueOfComp(Number, _numberComp); var _StringComp = createValueOfComp(String, _stringComp); var _wrappedComps = [ _BooleanComp, _DateComp, _NumberComp, _StringComp, ]; function tryWrappedComp(v1, v2) { var i = -1; var len = _wrappedComps.length; while (++i < len) { var comp = _wrappedComps[i]; if (comp.isComparable(v1) && comp.isComparable(v2)) { return comp.compare(v1, v2); } } return undefined; } function createObjectComp(keyComp, valueComp) { if (keyComp === void 0) { keyComp = anyFlatComp(); } if (valueComp === void 0) { valueComp = defaultComp(); } return { isComparable: function (obj) { return true; }, compare: function (v1, v2) { var keys1 = Object.keys(v1); var keys2 = Object.keys(v2); if (keys1.length === 0) { return keys2.length === 0 ? 0 : -1; } if (keys2.length === 0) { return keys1.length === 0 ? 0 : 1; } keys1.sort(keyComp.compare); keys2.sort(keyComp.compare); var length = Math.min(keys1.length, keys2.length); for (var index = 0; index < length; index++) { var key1 = keys1[index]; var key2 = keys2[index]; var keyResult = keyComp.compare(key1, key2); if (keyResult !== 0) return keyResult; var value1 = v1[key1]; var value2 = v2[key2]; var valueResult = valueComp.compare(value1, value2); if (valueResult !== 0) return valueResult; } var keyDiff = keys1.length - keys2.length; return keyDiff; }, }; } var _objectAnyComp = createObjectComp(); /** * Returns a Comp instance for objects that orders the object keys according to the given `keyComp`, and then compares the corresponding * values using the given `valueComp`. Objects are then compared as follows:<br/> * starting with the smallest key of either object:<br/> * - if only one of the objects has the key, the object with the key is considered to be larger than the other<br/> * - if both objects have the key, the values are compared with `valueComp`. If the values are not equal, this result is returned.<br/> * * if the objects have the same keys with the same values, they are considered equal<br/> * @param keyComp - (optional) the Comp instance used to order the object keys * @param valueComp - (optional) the Comp instance used to order the object values * @example * ```ts * const c = Comp.objectComp(); * console.log(c.compare({ a: 1 }, { a: 1 })) * // => 0 * console.log(c.compare({ a: 1 }, { a: 2 }) < 0) * // => true * console.log(c.compare({ b: 5 }, { a: 2 }) < 0) * // => true * console.log(c.compare({ a: 1, b: 2 }, { b: 5 }) < 0) * // => true * console.log(c.compare({ a: 1, b: 2 }, { b: 2, a: 1 })) * // => 0 * ``` */ function objectComp(options) { if (undefined === options) return _objectAnyComp; return createObjectComp(options.keyComp, options.valueComp); } Comp.objectComp = objectComp; function createAnyComp(mode) { return { isComparable: function (obj) { return true; }, compare: function (v1, v2) { if (Object.is(v1, v2)) return 0; var type1 = typeof v1; var type2 = typeof v2; if (type1 !== type2) { // we can only compare different types though strings return _anyToStringComp.compare(v1, v2); } switch (type1) { case 'bigint': return _bigIntComp.compare(v1, v2); case 'boolean': return _booleanComp.compare(v1, v2); case 'number': return _numberComp.compare(v1, v2); case 'string': return _stringComp.compare(v1, v2); case 'object': { if (null === v1) { if (null === v2) return 0; return -1; } if (null === v2) { return 1; } var wrappedComp = tryWrappedComp(v1, v2); if (undefined !== wrappedComp) return wrappedComp; if (mode !== 'FLAT') { if (_iterableAnyComp.isComparable(v1) && _iterableAnyComp.isComparable(v2)) { if (mode === 'SHALLOW') { return iterableComp(_anyFlatComp).compare(v1, v2); } return iterableComp(this).compare(v1, v2); } if (mode === 'SHALLOW') { return createObjectComp(_anyFlatComp, _anyFlatComp).compare(v1, v2); } return _objectAnyComp.compare(v1, v2); } } } return _anyToStringComp.compare(v1, v2); }, }; } /** * Returns a Comp instance that compares any value using default comparison functions, but never recursively compares * Iterables or objects. In those cases, it will use the stringComp instance. * @example * ```ts * const c = Comp.anyFlatComp(); * console.log(c.compare({ a: 1, b: 1 }, { b: 1, a: 1 }) < 0) * // => true * // First object is smaller because the objects are converted to a string with and then compares the resulting string. * ``` */ function anyFlatComp() { return _anyFlatComp; } Comp.anyFlatComp = anyFlatComp; /** * Returns a Comp instance that compares any value using default comparison functions. For Iterables and objects, their elements are compared * only one level deep for performance and to avoid infinite recursion. * @example * ```ts * const c = Comp.anyShallowComp(); * console.log(c.compare({ a: 1, b: 1 }, { b: 1, a: 1 })) * // => 0 * console.log(c.compare([{ a: 1, b: 1 }], [{ b: 1, a: 1 }]) < 0) * // => true * // First object is smaller because the objects are converted to a string and then compares the resulting string. * ``` */ function anyShallowComp() { return _anyShallowComp; } Comp.anyShallowComp = anyShallowComp; /** * Returns a Comp instance that compares any value using default comparison functions. For Iterables and objects, their elements are compared * recursively. * @note can become slow with large nested arrays and objects, and circular structures can cause infinite loops * @example * ```ts * const c = Comp.anyDeepComp(); * console.log(c.compare({ a: 1, b: 1 }, { b: 1, a: 1 })) * // => 0 * console.log(c.compare([{ a: 1, b: 1 }], [{ b: 1, a: 1 }])) * // => 0 * ``` */ function anyDeepComp() { return _anyDeepComp; } Comp.anyDeepComp = anyDeepComp; /** * Returns a Comp instance that extends the given `comp` instance with the capability to handle `undefined` values, where undefined is considered to be smaller * than any other value, and equal to another undefined. * @param comp - the Comp instance to wrap * @example * ```ts * const c = Comp.withUndefined(Comp.numberComp()) * console.log(c.compare(undefined, 5) < 0) * // => true * console.log(c.compare(undefined, undefined)) * // => 0 * ``` */ function withUndefined(comp) { return { isComparable: function (obj) { return undefined === obj || comp.isComparable(obj); }, compare: function (v1, v2) { if (undefined === v1) { if (undefined === v2) return 0; return -1; } if (undefined === v2) return 1; return comp.compare(v1, v2); }, }; } Comp.withUndefined = withUndefined; /** * Returns a Comp instance that extends the given `comp` instance with the capability to handle `null` values, where null is considered to be smaller * than any other value, and equal to another null. * @param comp - the Comp instance to wrap * @example * ```ts * const c = Comp.withNull(Comp.numberComp()) * console.log(c.compare(null, 5) < 0) * // => true * console.log(c.compare(null, null)) * // => 0 * ``` */ function withNull(comp) { return { isComparable: function (obj) { return null === obj || comp.isComparable(obj); }, compare: function (v1, v2) { if (null === v1) { if (null === v2) return 0; return -1; } if (null === v2) return 1; return comp.compare(v1, v2); }, }; } Comp.withNull = withNull; /** * Returns a Comp instance the reverses the order of the given `comp` instance. * @param comp - the Comp instance to wrap * @example * ```ts * const c = Comp.invert(Comp.numberComp()) * console.log(c.compare(3, 5) > 0) * // => true * console.log(c.compare(5, 5)) * // => 0 * ``` */ function invert(comp) { return { compare: function (v1, v2) { return comp.compare(v2, v1); }, isComparable: comp.isComparable, }; } Comp.invert = invert; /** * Returns an `Eq` equality instance thet will return true when the given `comp` comparable instance returns 0. * @param comp - the `Comp` comparable instance to convert * @example * ```ts * const eq = Comp.toEq(Comp.objectComp()) * console.log(eq({ a: 1, b: 2 }, { b: 2, a: 1 })) * // => true * ``` */ function toEq(comp) { return function (v1, v2) { return comp.compare(v1, v2) === 0; }; } Comp.toEq = toEq; })(Comp || (exports.Comp = Comp = {})); //# sourceMappingURL=comp.cjs.map