import _arrayFromIterator from "./_arrayFromIterator.js"; import _includesWith from "./_includesWith.js"; import _functionName from "./_functionName.js"; import _has from "./_has.js"; import _objectIs from "./_objectIs.js"; import keys from "../keys.js"; import type from "../type.js"; /** * private _uniqContentEquals function. * That function is checking equality of 2 iterator contents with 2 assumptions * - iterators lengths are the same * - iterators values are unique * * false-positive result will be returned for comparision of, e.g. * - [1,2,3] and [1,2,3,4] * - [1,1,1] and [1,2,3] * */ function _uniqContentEquals(aIterator, bIterator, stackA, stackB) { var a = _arrayFromIterator(aIterator); var b = _arrayFromIterator(bIterator); function eq(_a, _b) { return _equals(_a, _b, stackA.slice(), stackB.slice()); } // if *a* array contains any element that is not included in *b* return !_includesWith(function (b, aItem) { return !_includesWith(eq, aItem, b); }, b, a); } export default function _equals(a, b, stackA, stackB) { if (_objectIs(a, b)) { return true; } var typeA = type(a); if (typeA !== type(b)) { return false; } if (a == null || b == null) { return false; } if (typeof a['fantasy-land/equals'] === 'function' || typeof b['fantasy-land/equals'] === 'function') { return typeof a['fantasy-land/equals'] === 'function' && a['fantasy-land/equals'](b) && typeof b['fantasy-land/equals'] === 'function' && b['fantasy-land/equals'](a); } if (typeof a.equals === 'function' || typeof b.equals === 'function') { return typeof a.equals === 'function' && a.equals(b) && typeof b.equals === 'function' && b.equals(a); } switch (typeA) { case 'Arguments': case 'Array': case 'Object': if (typeof a.constructor === 'function' && _functionName(a.constructor) === 'Promise') { return a === b; } break; case 'Boolean': case 'Number': case 'String': if (!(typeof a === typeof b && _objectIs(a.valueOf(), b.valueOf()))) { return false; } break; case 'Date': if (!_objectIs(a.valueOf(), b.valueOf())) { return false; } break; case 'Error': return a.name === b.name && a.message === b.message; case 'RegExp': if (!(a.source === b.source && a.global === b.global && a.ignoreCase === b.ignoreCase && a.multiline === b.multiline && a.sticky === b.sticky && a.unicode === b.unicode)) { return false; } break; } var idx = stackA.length - 1; while (idx >= 0) { if (stackA[idx] === a) { return stackB[idx] === b; } idx -= 1; } switch (typeA) { case 'Map': if (a.size !== b.size) { return false; } return _uniqContentEquals(a.entries(), b.entries(), stackA.concat([a]), stackB.concat([b])); case 'Set': if (a.size !== b.size) { return false; } return _uniqContentEquals(a.values(), b.values(), stackA.concat([a]), stackB.concat([b])); case 'Arguments': case 'Array': case 'Object': case 'Boolean': case 'Number': case 'String': case 'Date': case 'Error': case 'RegExp': case 'Int8Array': case 'Uint8Array': case 'Uint8ClampedArray': case 'Int16Array': case 'Uint16Array': case 'Int32Array': case 'Uint32Array': case 'Float32Array': case 'Float64Array': case 'ArrayBuffer': break; default: // Values of other types are only equal if identical. return false; } var keysA = keys(a); if (keysA.length !== keys(b).length) { return false; } var extendedStackA = stackA.concat([a]); var extendedStackB = stackB.concat([b]); idx = keysA.length - 1; while (idx >= 0) { var key = keysA[idx]; if (!(_has(key, b) && _equals(b[key], a[key], extendedStackA, extendedStackB))) { return false; } idx -= 1; } return true; }