Skip to content

Commit

Permalink
[Breaking] compare Map, Set, WeakMap, WeakSet matching node a…
Browse files Browse the repository at this point in the history
…ssert

Fixes inspect-js#54. Fixes inspect-js#46.
  • Loading branch information
ljharb committed Nov 30, 2019
1 parent 20d2085 commit 2a4c42d
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 15 deletions.
4 changes: 4 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
"max-statements-per-line": [2, { "max": 2 }],
"strict": 1,
},
"globals": {
"WeakMap": false,
"WeakSet": false,
},
"overrides": [
{
"files": "example/**",
Expand Down
43 changes: 42 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ var isArray = require('isarray');
var isDate = require('is-date-object');
var whichBoxedPrimitive = require('which-boxed-primitive');
var callBound = require('es-abstract/helpers/callBound');
var whichCollection = require('which-collection');
var getIterator = require('es-get-iterator');

var $getTime = callBound('Date.prototype.getTime');
var gPO = Object.getPrototypeOf;
var $objToString = callBound('Object.prototype.toString');

var $mapHas = callBound('Map.prototype.has', true);
var $mapGet = callBound('Map.prototype.get', true);
var $setHas = callBound('Set.prototype.has', true);

function deepEqual(actual, expected, options) {
var opts = options || {};

Expand Down Expand Up @@ -62,7 +68,7 @@ function isBuffer(x) {
}

function objEquiv(a, b, opts) {
/* eslint max-statements: [2, 70], max-lines-per-function: [2, 80] */
/* eslint max-statements: [2, 100], max-lines-per-function: [2, 120], max-depth: [2, 5] */
var i, key;

if (typeof a !== typeof b) { return false; }
Expand Down Expand Up @@ -138,6 +144,41 @@ function objEquiv(a, b, opts) {
if (!deepEqual(a[key], b[key], opts)) { return false; }
}

var aCollection = whichCollection(a);
var bCollection = whichCollection(b);
if (aCollection !== bCollection) {
return false;
}
if (aCollection === 'Map' || aCollection === 'Set') {
var iA = getIterator(a);
var iB = getIterator(b);
var resultA;
var resultB;
if (aCollection === 'Map') { // aCollection === bCollection
var aWithBKey;
var bWithAKey;
while ((resultA = iA.next()) && (resultB = iB.next()) && !resultA.done && !resultB.done) {
if (!$mapHas(a, resultB.value[0]) || !$mapHas(b, resultA.value[0])) { return false; }
if (resultA.value[0] === resultB.value[0]) { // optimization: keys are the same, no need to look up values
if (!deepEqual(resultA.value[1], resultB.value[1])) { return false; }
} else {
aWithBKey = $mapGet(a, resultB.value[0]);
bWithAKey = $mapGet(b, resultA.value[0]);
if (!deepEqual(resultA.value[1], bWithAKey) || !deepEqual(resultB.value[1], aWithBKey)) {
return false;
}
}
}
} else if (aCollection === 'Set') { // aCollection === bCollection
while ((resultA = iA.next()) && (resultB = iB.next()) && !resultA.done && !resultB.done) {
if (!$setHas(a, resultB.value) || !$setHas(b, resultA.value)) { return false; }
}
}
if (resultA && resultB && resultA.done !== resultB.done) {
return false;
}
}

return true;
}

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@
},
"dependencies": {
"es-abstract": "^1.16.2",
"es-get-iterator": "^1.0.1",
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"isarray": "^2.0.5",
"object-is": "^1.0.1",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0",
"which-boxed-primitive": "^1.0.1"
"which-boxed-primitive": "^1.0.1",
"which-collection": "^1.0.0"
},
"devDependencies": {
"@ljharb/eslint-config": "^15.0.2",
Expand Down
97 changes: 84 additions & 13 deletions test/cmp.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,110 @@ test('equal', function (t) {
false
);

t.end();
});

test('Maps', { skip: typeof Map !== 'function' }, function (t) {
t.deepEqualTest(
new Map([['a', 1], ['b', 2]]),
new Map([['b', 2], ['a', 1]]),
'two equal Maps',
true,
true,
true
);

t.deepEqualTest(
new Map([['a', [1, 2]]]),
new Map([['a', [2, 1]]]),
'two Maps with inequal values on the same key',
false,
false
);

t.deepEqualTest(
new Map([['a', 1]]),
new Map([['b', 1]]),
'two inequal Maps',
false,
false
);

t.end();
});

test('not equal', function (t) {
test('WeakMaps', { skip: typeof WeakMap !== 'function' }, function (t) {
t.deepEqualTest(
{ x: 5, y: [6] },
{ x: 5, y: 6 },
'two inequal objects are',
false,
false
new WeakMap([[Object, null], [Function, true]]),
new WeakMap([[Function, true], [Object, null]]),
'two equal WeakMaps',
true,
true
);

t.deepEqualTest(
new Map([['a', [1, 2]]]),
new Map([['a', [2, 1]]]),
'two Maps with inequal values on the same key',
new WeakMap([[Object, null]]),
new WeakMap([[Object, true]]),
'two WeakMaps with inequal values on the same key',
true,
true
);

t.deepEqualTest(
new WeakMap([[Object, null], [Function, true]]),
new WeakMap([[Object, null]]),
'two inequal WeakMaps',
true,
true
);

t.end();
});

test('Sets', { skip: typeof Set !== 'function' }, function (t) {
t.deepEqualTest(
new Set(['a', 1, 'b', 2]),
new Set(['b', 2, 'a', 1]),
'two equal Sets',
true,
true
);

t.deepEqualTest(
new Set(['a', 1]),
new Set(['b', 1]),
'two inequal Sets',
false,
false
);

t.end();
});

test('WeakSets', { skip: typeof WeakSet !== 'function' }, function (t) {
t.deepEqualTest(
new Map([['a', 1]]),
new Map([['b', 1]]),
'two inequal Maps',
new WeakSet([Object, Function]),
new WeakSet([Function, Object]),
'two equal WeakSets',
true,
true
);

t.deepEqualTest(
new WeakSet([Object, Function]),
new WeakSet([Object]),
'two inequal WeakSets',
true,
true
);

t.end();
});

test('not equal', function (t) {
t.deepEqualTest(
{ x: 5, y: [6] },
{ x: 5, y: 6 },
'two inequal objects are',
false,
false
);
Expand Down

0 comments on commit 2a4c42d

Please sign in to comment.