Skip to content

Commit

Permalink
performance updates & added deep obj & arr check
Browse files Browse the repository at this point in the history
Tim M committed Dec 22, 2020
1 parent 9792a74 commit 6d5ef46
Showing 5 changed files with 147 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ This is a very ✌ lightweight and ⚡️ fast library for comparing **objects**

- Provides **Created** and **Deleted** values between two arrays of primitives (strings, numbers, etc.) using the ***compareArrayVals*** function.

- Lastly, has a shallow (1 level deep) object comparing helper function for quick equivalence checks using the ***isEqualObject*** function.
- Lastly, has a deep Object and Array comparing helper functions for quick equivalence checks using the ***isEqualObject*** or ***isEqualArray*** function. *Note: [Object.is()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) is used for primative data type comparison checks.

## Install

94 changes: 72 additions & 22 deletions index.test.js
Original file line number Diff line number Diff line change
@@ -10,19 +10,29 @@ const newArrayOfObjects = [
];

describe('compareObjectVals Tests', () => {
test('compareObjectVals functionality', () => {
const { createdVals, updatedVals, deletedVals } = compareObjectVals(
[
originalArrayOfObjects,
newArrayOfObjects
],
'commonKey'
);

expect(createdVals).toStrictEqual([{'commonKey': 7}]);
expect(updatedVals).toStrictEqual([{'commonKey': 2, 'newkey': 'test'}]);
expect(deletedVals).toStrictEqual([{'commonKey': 1, 'anotherkey': 'test'}]);
});
test('basic compareObjectVals functionality', () => {
const { createdVals, updatedVals, deletedVals } = compareObjectVals(
[
originalArrayOfObjects,
newArrayOfObjects
],'commonKey');
expect(deletedVals).toStrictEqual([{'commonKey': 1, 'anotherkey': 'test'}]);
expect(updatedVals).toStrictEqual([{'commonKey': 2, 'newkey': 'test'}]);
expect(createdVals).toStrictEqual([{'commonKey': 7}]);
});

test('compareObjectVals functionality w/ nested objects', () => {
const orgOb1 = [
{'test': 11, 'key2': 22}, {'test': 23, 1: [1,3]}, {'test': [{'test33': 22}, {1: 22}]}
];
const newOb1 = [{'test': 11, 'key2': 33}, {'test': 22}, {'test': 23, 1: [1,8,3]}];
const {
createdVals, updatedVals, deletedVals
} = compareObjectVals([orgOb1, newOb1], 'test');
expect(deletedVals).toStrictEqual([{'test': [{'test33': 22}, {1: 22}]}]);
expect(updatedVals).toStrictEqual([{'test': 11, 'key2': 33}, {'test': 23, 1: [1,8,3]}]);
expect(createdVals).toStrictEqual([{'test': 22}]);
});

test('empty original array of objects', () => {
const { createdVals, updatedVals, deletedVals } = compareObjectVals([[], newArrayOfObjects], 'commonKey');
@@ -129,18 +139,58 @@ const ob1 = { 'test': 1, 'test2': 2 };
const ob2 = { 'test': 1, 'test2': 2 };
const ob3 = { 'a': undefined };
const ob4 = { 'b': 'something' };
const ob5 = { 'a': Number.NaN };
const ob6 = { 'a': NaN };
const ob7 = { 'a': 'test', b: 5, c: 22, d: undefined };
const ob8 = { 'a': 'test', b: 55, c: 22, d: undefined };

describe('helper isEqualObject comparer tests', () => {

test('isEqualObject truthy tests', () => {
expect(isEqualObject(ob1, ob2)).toBe(true);
expect(isEqualObject(ob3, ob3)).toBe(true);
});
test('isEqualObject basic truthy tests', () => {
expect(isEqualObject(ob1, ob2)).toBe(true);
expect(isEqualObject(ob3, ob3)).toBe(true);
expect(isEqualObject(ob5, ob6)).toBe(true);
});

test('isEqualObject falsy tests', () => {
expect(isEqualObject(originalArrayOfObjects[0], newArrayOfObjects[0])).toBe(false);
expect(isEqualObject(ob3, ob4)).toBe(false);
expect(isEqualObject(ob4, ob3)).toBe(false);
});
test('isEqualObject basic falsy tests', () => {
expect(isEqualObject(originalArrayOfObjects[0], newArrayOfObjects[0])).toBe(false);
expect(isEqualObject(ob3, ob4)).toBe(false);
expect(isEqualObject(ob4, ob3)).toBe(false);
expect(isEqualObject(ob7, ob8)).toBe(false);
});

test('isEqualObject with nested objects containing array of objects', () => {
const oba1 = {a: 1, b: 2, c: {'one': 1, 'two': [{ 2: 1, 44:1 }]}};
const oba2 = {a: 1, b: 2, c: {'one': 1, 'two': [{ 2: 1, 44:1 }]}};
const oba3 = {a: 1, b: 2, c: {'one': 1, 'two': [{ 2: 1, 44:3 }]}};
const oba4 = {a: 1, b: 2, c: {'one': 1, 'two': [{ 44:1 }]}};
const oba5 = {a: 1, b: 2, c: {'one': 1, 'two': [22]}};
const oba6 = {a: 1, b: 2, c: {'one': 1, 'two': [22]}};
expect(isEqualObject(oba1, oba2)).toBe(true);
expect(isEqualObject(oba2, oba3)).toBe(false);
expect(isEqualObject(oba3, oba4)).toBe(false);
expect(isEqualObject(oba4, oba5)).toBe(false);
expect(isEqualObject(oba5, oba6)).toBe(true);
})

test('isEqualObject with nested basic identical array', () => {
const obb1 = {a: 1, b: [2]};
const obb2 = {a: 1, b: [2]};
const obb3 = {a: 1, b: [2, 33]};
const obb4 = {a: 1, b: [2, 33]};
const obb5 = {a: 1, b: {1: 33}};
const obb6 = {a: 1, b: {1: 33, 5: 11}};
const obb7 = {a: 1, b: {1: 33, 5: undefined}};
const obb8 = {a: 1, b: {1: 33, null: 3}};
const obb9 = {a: 1, b: {1: 33, null: 3}};
expect(isEqualObject(obb1, obb2)).toBe(true);
expect(isEqualObject(obb2, obb3)).toBe(false);
expect(isEqualObject(obb3, obb4)).toBe(true);
expect(isEqualObject(obb4, obb5)).toBe(false);
expect(isEqualObject(obb5, obb6)).toBe(false);
expect(isEqualObject(obb6, obb7)).toBe(false);
expect(isEqualObject(obb7, obb8)).toBe(false);
expect(isEqualObject(obb8, obb9)).toBe(true);
})

});
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "crud-compare",
"version": "2.0.6",
"version": "2.1.0",
"description": "Helper utilities to compare objects or arrays for obtaining created, updated, & deleted values.",
"main": "dist/index.js",
"repository": {
99 changes: 72 additions & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -3,23 +3,61 @@
*/

/**
* Check if two objects are equal
* Check if two objects are equal w/ deep comparison
* @param {Object} a
* @param {Object} b
*/
function isEqualObject (a: Object, b: Object) {
function isEqualObject (a: Object, b: Object): Boolean {
if (Array.isArray(a) && Array.isArray(b)) return isEqualArray(a, b);
if (typeof a !== 'object' && typeof b !== 'object') return Object.is(a, b);
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
if (aProps.length !== bProps.length) return false;

for (var i = 0; i < aProps.length; i++) {
var i = 0;
while (i < aProps.length) {
var propName = aProps[i];

if (bProps.indexOf(propName) == -1 || a[propName] !== b[propName]) {
if (typeof a[propName] == 'object' && typeof b[propName] == 'object') {
if (
Array.isArray(a[propName]) &&
Array.isArray(b[propName])) {
if (!isEqualArray(a[propName], b[propName])) return false;
}
else if (!isEqualObject(a[propName], b[propName])) return false;
}
else if (
bProps.indexOf(propName) == -1 ||
!Object.is(a[propName], b[propName])) {
return false;
}
i++;
}
return true;
}

/**
* Check if two arrays are equal w/ deep comparison
* @param {Object} a
* @param {Object} b
*/
function isEqualArray (a: any[], b: any[]): Boolean {
if (a.length !== b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (
typeof a[i] !== 'object' &&
b.indexOf(a[i]) === -1) return false;
else if (
typeof a[i] === 'object' &&
!Array.isArray(a[i]) &&
!isEqualObject(a[i], b[i])
) return false;
else if (
Array.isArray(a[i]) &&
Array.isArray(b[i]) &&
!isEqualArray(a[i], b[i])
) return false;
}
return true;
}

@@ -39,10 +77,10 @@ function compareObjectVals (toCompareVals: [Object[], Object[]], key: string) :
var createdVals: any[] | null = [];
var updatedVals: any[] | null = [];
var deletedVals: any[] | null = [];
var originalItemKeys: any[] = [];
var activeItemKeys: any[] = [];
var originalItem = toCompareVals[0];
var activeItem = toCompareVals[1];
var originalItem: Object[] = toCompareVals[0];
var activeItem: Object[] = toCompareVals[1];
var i: number = 0, j: number = 0,
originalSameKeyValue: Boolean = false, activeSameKeyValue: Boolean[] = [];

if (!originalItem.length) {
return {
@@ -52,25 +90,32 @@ function compareObjectVals (toCompareVals: [Object[], Object[]], key: string) :
};
}

for (var i = 0; i < originalItem.length; i++) {
var outerKeyVal = originalItem[i][key];
originalItemKeys.push(outerKeyVal);
while (i < originalItem.length) {
j = 0;

while (j < activeItem.length) {
var sameKeyVal = isEqualObject(originalItem[i][key], activeItem[j][key]);
originalSameKeyValue = sameKeyVal || originalSameKeyValue;
activeSameKeyValue[j] = sameKeyVal || activeSameKeyValue[j] || false;
var isLastOriginalItemRun = i === originalItem.length - 1;

for (var j = 0; j < activeItem.length; j++) {
if (i === 0) {
activeItemKeys.push(activeItem[j][key]);
if (isLastOriginalItemRun && !activeSameKeyValue[j]) {
createdVals.push(activeItem[j]);
}

if (i === originalItem.length - 1 && originalItemKeys.indexOf(activeItem[j][key]) === -1) {
createdVals.push(activeItem[j]);
} else if (originalItem[i][key] === activeItem[j][key] && !isEqualObject(originalItem[i], activeItem[j])) {
else if (
isLastOriginalItemRun &&
activeSameKeyValue[j] &&
!isEqualObject(originalItem[i], activeItem[j])) {
updatedVals.push(activeItem[j]);
}
j++;
}

if (activeItemKeys.indexOf(outerKeyVal) === -1) {
if (!originalSameKeyValue) {
deletedVals.push(originalItem[i]);
}
originalSameKeyValue = false;
i++;
}

return {
@@ -80,7 +125,7 @@ function compareObjectVals (toCompareVals: [Object[], Object[]], key: string) :
};
}

function handleInputValidation(toCompareVals, key) {
function handleInputValidation(toCompareVals: [Object[], Object[]], key: string) {
if (!Array.isArray(toCompareVals) || typeof key !== 'string') {
throw new TypeError(`toCompareVals must be an array of the originalArray
and stateUpdatedArray you want to compare, and the key must be of type string!`)
@@ -91,14 +136,14 @@ function handleInputValidation(toCompareVals, key) {
if (!Array.isArray(toCompareVals[0]) || !Array.isArray(toCompareVals[1])) {
throw new TypeError(`The originalArray and stateUpdatedArray must both be arrays!`);
}
const firstElementExists = toCompareVals[0] && toCompareVals[0][0];
const secondElementExits = toCompareVals[1] && toCompareVals[1][0];
const firstElementIsNotArrayOfObjects =
var firstElementExists = toCompareVals[0] && toCompareVals[0][0];
var secondElementExits = toCompareVals[1] && toCompareVals[1][0];
var firstElementIsNotArrayOfObjects =
(
firstElementExists && toCompareVals[0][0] !== Object(toCompareVals[0][0]) ||
firstElementExists && Array.isArray(toCompareVals[0][0])
)
const secondElementIsNotArrayOfObjects =
var secondElementIsNotArrayOfObjects =
(
secondElementExits && toCompareVals[1][0] !== Object(toCompareVals[1][0]) ||
secondElementExits && Array.isArray(toCompareVals[1][0])
@@ -143,4 +188,4 @@ function compareArrayVals (toCompareVals: [any[], any[]]) : { createdVals: any[]
};
}

export { isEqualObject, compareArrayVals, compareObjectVals };
export { isEqualObject, isEqualArray, compareArrayVals, compareObjectVals };

0 comments on commit 6d5ef46

Please sign in to comment.