Add a core deepEqual function (#1018)

* Add a core deepEqual function

* Add tests
This commit is contained in:
toasted-nutbread 2020-11-09 21:47:25 -05:00 committed by GitHub
parent eb8069a494
commit 219dfb4917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 198 additions and 1 deletions

View File

@ -110,6 +110,7 @@
"escapeRegExp": "readonly",
"deferPromise": "readonly",
"clone": "readonly",
"deepEqual": "readonly",
"generateId": "readonly",
"promiseAnimationFrame": "readonly",
"DynamicProperty": "readonly",

View File

@ -176,6 +176,72 @@ const clone = (() => {
return clone;
})();
const deepEqual = (() => {
// eslint-disable-next-line no-shadow
function deepEqual(value1, value2) {
if (value1 === value2) { return true; }
const type = typeof value1;
if (typeof value2 !== type) { return false; }
switch (type) {
case 'object':
case 'function':
return deepEqualInternal(value1, value2, new Set());
default:
return false;
}
}
function deepEqualInternal(value1, value2, visited1) {
if (value1 === value2) { return true; }
const type = typeof value1;
if (typeof value2 !== type) { return false; }
switch (type) {
case 'object':
case 'function':
{
if (value1 === null || value2 === null) { return false; }
const array = Array.isArray(value1);
if (array !== Array.isArray(value2)) { return false; }
if (visited1.has(value1)) { return false; }
visited1.add(value1);
return array ? areArraysEqual(value1, value2, visited1) : areObjectsEqual(value1, value2, visited1);
}
default:
return false;
}
}
function areObjectsEqual(value1, value2, visited1) {
const keys1 = Object.keys(value1);
const keys2 = Object.keys(value2);
if (keys1.length !== keys2.length) { return false; }
const keys1Set = new Set(keys1);
for (const key of keys2) {
if (!keys1Set.has(key) || !deepEqualInternal(value1[key], value2[key], visited1)) { return false; }
}
return true;
}
function areArraysEqual(value1, value2, visited1) {
const length = value1.length;
if (length !== value2.length) { return false; }
for (let i = 0; i < length; ++i) {
if (!deepEqualInternal(value1[i], value2[i], visited1)) { return false; }
}
return true;
}
return deepEqual;
})();
function generateId(length) {
const array = new Uint8Array(length);
crypto.getRandomValues(array);

View File

@ -32,7 +32,7 @@ const vm = new VM({
vm.execute([
'mixed/js/core.js'
]);
const [DynamicProperty] = vm.get(['DynamicProperty']);
const [DynamicProperty, deepEqual] = vm.get(['DynamicProperty', 'deepEqual']);
function testDynamicProperty() {
@ -161,9 +161,139 @@ function testDynamicProperty() {
}
}
function testDeepEqual() {
const data = [
// Simple tests
{
value1: 0,
value2: 0,
expected: true
},
{
value1: null,
value2: null,
expected: true
},
{
value1: 'test',
value2: 'test',
expected: true
},
{
value1: true,
value2: true,
expected: true
},
{
value1: 0,
value2: 1,
expected: false
},
{
value1: null,
value2: false,
expected: false
},
{
value1: 'test1',
value2: 'test2',
expected: false
},
{
value1: true,
value2: false,
expected: false
},
// Simple object tests
{
value1: {},
value2: {},
expected: true
},
{
value1: {},
value2: [],
expected: false
},
{
value1: [],
value2: [],
expected: true
},
{
value1: {},
value2: null,
expected: false
},
// Complex object tests
{
value1: [1],
value2: [],
expected: false
},
{
value1: [1],
value2: [1],
expected: true
},
{
value1: [1],
value2: [2],
expected: false
},
{
value1: {},
value2: {test: 1},
expected: false
},
{
value1: {test: 1},
value2: {test: 1},
expected: true
},
{
value1: {test: 1},
value2: {test: {test2: false}},
expected: false
},
{
value1: {test: {test2: true}},
value2: {test: {test2: false}},
expected: false
},
{
value1: {test: {test2: [true]}},
value2: {test: {test2: [true]}},
expected: true
},
// Recursive
{
value1: (() => { const x = {}; x.x = x; return x; })(),
value2: (() => { const x = {}; x.x = x; return x; })(),
expected: false
}
];
let index = 0;
for (const {value1, value2, expected} of data) {
const actual1 = deepEqual(value1, value2);
assert.strictEqual(actual1, expected, `Failed for test ${index}`);
const actual2 = deepEqual(value2, value1);
assert.strictEqual(actual2, expected, `Failed for test ${index}`);
++index;
}
}
function main() {
testDynamicProperty();
testDeepEqual();
}