Repository URL to install this package:
|
Version:
2.4.3 ▾
|
// Based on Underscore.js 1.8.3 (http://underscorejs.org)
import { isObject } from "./types";
const { hasOwnProperty } = Object.prototype;
export const equals = Symbol("equals");
function is_Equatable(obj) {
return isObject(obj) && obj[equals] !== undefined;
}
export const wildcard = Symbol("wildcard");
const toString = Object.prototype.toString;
export class Comparator {
constructor() {
this.a_stack = [];
this.b_stack = [];
}
eq(a, b) {
if (Object.is(a, b))
return true;
if (a === wildcard || b === wildcard)
return true;
if (a == null || b == null)
return a === b;
const class_name = toString.call(a);
if (class_name != toString.call(b))
return false;
switch (class_name) {
case "[object Number]":
return this.numbers(a, b);
case "[object RegExp]":
case "[object String]":
return `${a}` == `${b}`;
case "[object Date]":
case "[object Boolean]":
return +a === +b;
}
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// Initializing stack of traversed objects.
// It's done here since we only need them for objects and arrays comparison.
const { a_stack, b_stack } = this;
let length = a_stack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (a_stack[length] === a)
return b_stack[length] === b;
}
a_stack.push(a);
b_stack.push(b);
const result = (() => {
if (is_Equatable(a) && is_Equatable(b)) {
return a[equals](b, this);
}
switch (class_name) {
case "[object Array]":
case "[object Uint8Array]":
case "[object Int8Array]":
case "[object Uint16Array]":
case "[object Int16Array]":
case "[object Uint32Array]":
case "[object Int32Array]":
case "[object Float32Array]":
case "[object Float64Array]": {
return this.arrays(a, b);
}
case "[object Map]":
return this.maps(a, b);
case "[object Set]":
return this.sets(a, b);
case "[object Object]": {
if (a.constructor == b.constructor && (a.constructor == null || a.constructor === Object)) {
return this.objects(a, b);
}
}
case "[object Function]": {
if (a.constructor == b.constructor && a.constructor === Function) {
return this.eq(`${a}`, `${b}`);
}
}
}
if (a instanceof Node) {
return this.nodes(a, b);
}
throw Error(`can't compare objects of type ${class_name}`);
})();
a_stack.pop();
b_stack.pop();
return result;
}
numbers(a, b) {
return Object.is(a, b);
}
arrays(a, b) {
const { length } = a;
if (length != b.length)
return false;
for (let i = 0; i < length; i++) {
if (!this.eq(a[i], b[i]))
return false;
}
return true;
}
iterables(a, b) {
const ai = a[Symbol.iterator]();
const bi = b[Symbol.iterator]();
while (true) {
const an = ai.next();
const bn = bi.next();
if (an.done && bn.done)
return true;
if (an.done || bn.done)
return false;
if (!this.eq(an.value, bn.value))
return false;
}
}
maps(a, b) {
if (a.size != b.size)
return false;
for (const [key, val] of a) {
if (!b.has(key) || !this.eq(val, b.get(key)))
return false;
}
return true;
}
sets(a, b) {
if (a.size != b.size)
return false;
for (const key of a) {
if (!b.has(key))
return false;
}
return true;
}
objects(a, b) {
const keys = Object.keys(a);
if (keys.length != Object.keys(b).length)
return false;
for (const key of keys) {
if (!hasOwnProperty.call(b, key) || !this.eq(a[key], b[key]))
return false;
}
return true;
}
nodes(a, b) {
if (a.nodeType != b.nodeType)
return false;
if (a.textContent != b.textContent)
return false;
if (!this.iterables(a.childNodes, b.childNodes))
return false;
return true;
}
}
Comparator.__name__ = "Comparator";
const { abs } = Math;
export class SimilarComparator extends Comparator {
constructor(tolerance = 1e-4) {
super();
this.tolerance = tolerance;
}
numbers(a, b) {
return super.numbers(a, b) || abs(a - b) < this.tolerance;
}
}
SimilarComparator.__name__ = "SimilarComparator";
export function is_equal(a, b) {
const comparator = new Comparator();
return comparator.eq(a, b);
}
export function is_similar(a, b, tolerance) {
const comparator = new SimilarComparator(tolerance);
return comparator.eq(a, b);
}
/** @deprecated */
export const isEqual = is_equal;
//# sourceMappingURL=eq.js.map