Add a core deepEqual function (#1018)
* Add a core deepEqual function * Add tests
This commit is contained in:
parent
eb8069a494
commit
219dfb4917
@ -110,6 +110,7 @@
|
|||||||
"escapeRegExp": "readonly",
|
"escapeRegExp": "readonly",
|
||||||
"deferPromise": "readonly",
|
"deferPromise": "readonly",
|
||||||
"clone": "readonly",
|
"clone": "readonly",
|
||||||
|
"deepEqual": "readonly",
|
||||||
"generateId": "readonly",
|
"generateId": "readonly",
|
||||||
"promiseAnimationFrame": "readonly",
|
"promiseAnimationFrame": "readonly",
|
||||||
"DynamicProperty": "readonly",
|
"DynamicProperty": "readonly",
|
||||||
|
@ -176,6 +176,72 @@ const clone = (() => {
|
|||||||
return 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) {
|
function generateId(length) {
|
||||||
const array = new Uint8Array(length);
|
const array = new Uint8Array(length);
|
||||||
crypto.getRandomValues(array);
|
crypto.getRandomValues(array);
|
||||||
|
@ -32,7 +32,7 @@ const vm = new VM({
|
|||||||
vm.execute([
|
vm.execute([
|
||||||
'mixed/js/core.js'
|
'mixed/js/core.js'
|
||||||
]);
|
]);
|
||||||
const [DynamicProperty] = vm.get(['DynamicProperty']);
|
const [DynamicProperty, deepEqual] = vm.get(['DynamicProperty', 'deepEqual']);
|
||||||
|
|
||||||
|
|
||||||
function testDynamicProperty() {
|
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() {
|
function main() {
|
||||||
testDynamicProperty();
|
testDynamicProperty();
|
||||||
|
testDeepEqual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user