UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

173 lines (172 loc) 6.47 kB
import { RequiredError } from "../error/RequiredError.js"; import { isArray } from "./array.js"; import { isMap } from "./map.js"; import { isObject, isProp } from "./object.js"; import { compareAscending } from "./sort.js"; /** Assert two values are equal. */ export function assertEqual(left, right, caller = assertEqual) { if (left !== right) new RequiredError("Must be equal", { left, right, caller }); } /** Assert two values are equal. */ export function assertNot(left, right, caller = assertNot) { if (left === right) new RequiredError("Must not be equal", { left, right, caller }); } /** Is unknown value `left` exactly equal to `right`? */ export function isEqual(left, right) { return left === right; } /** Is unknown value `left` not exactly equal to `right`? */ export function notEqual(left, right) { return left !== right; } /** Is unknown value `left` less than `right`? */ export function isLess(left, right) { return compareAscending(left, right) < 0; } /** Is unknown value `left` less than or equal to `right`? */ export function isEqualLess(left, right) { return compareAscending(left, right) <= 0; } /** Is unknown value `left` greater than `right`? */ export function isGreater(left, right) { return compareAscending(left, right) > 0; } /** Is unknown value `left` greater than or equal to `right`? */ export function isEqualGreater(left, right) { return compareAscending(left, right) >= 0; } // Internal shared by shallow/deep equal. function _isEqualRecursively(left, right, recursor) { if (left === right) return true; if (isObject(right)) { if (isArray(right)) return isArray(left) ? isArrayEqual(left, right, recursor) : false; if (isMap(right)) return isMap(left) ? isMapEqual(left, right, recursor) : false; return isObject(left) ? isObjectEqual(left, right, recursor) : false; } return false; } /** * Are two unknown values shallowly equal? * - If the values are both arrays/objects, see if the items/properties are **shallowly** equal with each other. */ export function isShallowEqual(left, right) { return _isEqualRecursively(left, right, isEqual); } /** Are two unknown values not shallowly equal? */ export function notShallowEqual(left, right) { return !isShallowEqual(left, right); } /** * Are two unknown values deeply equal? * - If the values are both arrays/objects, see if the items/properties are **deeply** equal with each other. */ export function isDeepEqual(left, right) { return _isEqualRecursively(left, right, isDeepEqual); } /** Are two unknown values not deeply equal? */ export function notDeepEqual(left, right) { return !isShallowEqual(left, right); } /** * Are two maps equal (based on their items). */ export function isMapEqual(left, right, recursor = isEqual) { if (left === right) return true; // Referentially equal. if (left.size !== right.size) return false; // Different lengths aren't equal. const leftIterator = left.entries(); for (const r of right) { const { done, value: l } = leftIterator.next(); if (done) return false; if (l[0] !== r[0] || !recursor(l[1], r[1])) return false; } return true; } /** * Are two arrays equal based on their items? * * @param recursor Function that checks each item of the array. * - Defaults to `isEqual()` to check strict equality of the items. * - Use `isDeepEqual()` as the recursor to check to check deep equality of the items. */ export function isArrayEqual(left, right, recursor = isEqual) { if (left === right) return true; // Referentially equal. if (left.length !== right.length) return false; // Different lengths aren't equal. for (const [k, r] of right.entries()) if (!recursor(left[k], r)) return false; return true; } /** Is unknown value `left` in array `right`? */ export function isInArray(left, right) { return right.includes(left); } /** Is unknown value `left` not in array `right`? */ export function notInArray(left, right) { return !isInArray(left, right); } /** Is unknown value `left` an array including `right`? */ export function isArrayWith(left, right) { return isArray(left) && left.includes(right); } /** Is unknown value `left` not an array or does not include `right`? */ export function notArrayWith(left, right) { return !notArrayWith(left, right); } /** * Are two objects equal based on their own props? * - `left` must have every property present in `right` * - `left` must not have excess properties not present in `right` * * @param recursor Function that checks each prop of the object. * - Defaults to `isEqual()` to check strict equality of the properties. * - Use `isDeepEqual()` as the recursor to check to check deep equality of the properties. */ export function isObjectEqual(left, right, recursor = isEqual) { if (left === right) return true; // Referentially equal. const rightEntries = Object.entries(right); const leftKeys = Object.keys(left); if (rightEntries.length !== leftKeys.length) return false; // Different lengths aren't equal. for (const [k, r] of rightEntries) if (!isProp(left, k) || !recursor(left[k], r)) return false; return true; } /** * Are two objects partially equal based on their own props? * - `left` must have every property present in `right` * - `left` may have have excess properties not present in `right` * * @param recursor Function that checks each prop of the object. * - Defaults to `isEqual()` to check strict equality of the properties. * - Use `isDeepEqual()` as the recursor to check to check deep equality of the properties. */ export function isObjectMatch(left, right, recursor = isEqual) { if (left === right) return true; // Referentially equal. const rightEntries = Object.entries(right); for (const [k, r] of rightEntries) if (!isProp(left, k) || !recursor(left[k], r)) return false; return true; } /** Is unknown value `left` an object with every prop from `right`? */ export function isObjectWith(left, right) { return isObject(left) && isObjectMatch(left, right); } /** Is unknown value `left` not an object or missing one or more props from `right`? */ export function notObjectWith(left, right) { return !isObjectWith(left, right); }