shelving
Version:
Toolkit for using data in JavaScript.
173 lines (172 loc) • 6.47 kB
JavaScript
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);
}