Browse Source

feat: detect added and deleted properties in deep nested objects

pull/6/head
DoneDeal0 2 years ago
parent
commit
3eb866ff7c
  1. 1
      src/index.ts
  2. 87
      src/object-diff.ts
  3. 18
      test/object-diff.test.ts

1
src/index.ts

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
export { getObjectDiff } from "./object-diff";
export { getListDiff } from "./list-diff";
export { isEqual, isObject } from "./utils";

87
src/object-diff.ts

@ -59,27 +59,69 @@ function formatSingleObjectDiff( @@ -59,27 +59,69 @@ function formatSingleObjectDiff(
}
function getPreviousMatch(
prevSubValues: [string, any][] | null,
previousValue: any | undefined,
nextSubProperty: any
): any | undefined {
if (!prevSubValues) {
if (!previousValue) {
return undefined;
}
const previousMatch = prevSubValues.find(([subPreviousKey]) =>
const previousMatch = Object.entries(previousValue).find(([subPreviousKey]) =>
isEqual(subPreviousKey, nextSubProperty)
);
return previousMatch ? previousMatch[1] : undefined;
}
function getValueStatus(previousValue: any, nextValue: any): DiffStatus {
if (isEqual(previousValue, nextValue)) {
return STATUS.EQUAL;
}
return STATUS.UPDATED;
}
function getPropertyStatus(subPropertiesDiff: Subproperties[]): DiffStatus {
return subPropertiesDiff.some((property) => property.status !== STATUS.EQUAL)
? STATUS.UPDATED
: STATUS.EQUAL;
}
function getDeletedProperties(
previousValue: any,
nextValue: any,
nextProperty: string
) {
if (!previousValue) return undefined;
const previousMatch = previousValue[nextProperty];
if (!previousMatch) return undefined;
const nextMatch = nextValue[nextProperty];
const nextKeys = isObject(nextMatch) ? Object.keys(nextMatch) : [];
const prevKeys = isObject(previousMatch) ? Object.keys(previousMatch) : [];
const deletedKeys = prevKeys.filter(
(previousKey) => !nextKeys.includes(previousKey)
);
const result = deletedKeys.map((deletedKey) => ({
property: deletedKey,
value: previousMatch[deletedKey],
}));
if (result.length > 0) return result;
return undefined;
}
function getSubPropertiesDiff(
previousValue: Record<string, any> | undefined,
nextValue: Record<string, any>
): Subproperties[] {
const subPropertiesDiff: Subproperties[] = [];
const prevSubValues = previousValue ? Object.entries(previousValue) : null;
let subDiff: Subproperties["subDiff"];
let subDiff: Subproperties[];
Object.entries(nextValue).forEach(([nextSubProperty, nextSubValue]) => {
const previousMatch = getPreviousMatch(prevSubValues, nextSubProperty);
const previousMatch = getPreviousMatch(previousValue, nextSubProperty);
if (!!!previousMatch && !!nextSubProperty) {
return subPropertiesDiff.push({
name: nextSubProperty,
previousValue: undefined,
currentValue: nextSubValue,
status: STATUS.ADDED,
});
}
if (isObject(nextSubValue)) {
const data: Subproperties[] = getSubPropertiesDiff(
previousMatch,
@ -88,15 +130,33 @@ function getSubPropertiesDiff( @@ -88,15 +130,33 @@ function getSubPropertiesDiff(
if (data && data.length > 0) {
subDiff = data;
}
const deletedProperties = getDeletedProperties(
previousValue,
nextValue,
nextSubProperty
);
if (deletedProperties) {
deletedProperties.forEach((deletedProperty) => {
const deletedData = {
name: deletedProperty.property,
previousValue: deletedProperty.value,
currentValue: undefined,
status: STATUS.DELETED,
};
if (subDiff) {
subDiff.push(deletedData);
} else {
subDiff = [deletedData];
}
});
}
}
if (previousMatch) {
subPropertiesDiff.push({
name: nextSubProperty,
previousValue: previousMatch,
currentValue: nextSubValue,
status: isEqual(previousMatch, nextSubValue)
? STATUS.EQUAL
: STATUS.UPDATED,
status: getValueStatus(previousMatch, nextSubValue),
...(!!subDiff && { subDiff }),
});
}
@ -130,16 +190,11 @@ export function getObjectDiff( @@ -130,16 +190,11 @@ export function getObjectDiff(
previousValue,
nextValue
);
const _status = subPropertiesDiff.some(
(property) => property.status !== STATUS.EQUAL
)
? STATUS.UPDATED
: STATUS.EQUAL;
return diff.push({
property: nextProperty,
previousValue,
currentValue: nextValue,
status: _status,
status: getPropertyStatus(subPropertiesDiff),
subPropertiesDiff,
});
}
@ -147,7 +202,7 @@ export function getObjectDiff( @@ -147,7 +202,7 @@ export function getObjectDiff(
property: nextProperty,
previousValue,
currentValue: nextValue,
status: previousValue === nextValue ? STATUS.EQUAL : STATUS.UPDATED,
status: getValueStatus(previousValue, nextValue),
});
});
return {

18
test/object-diff.test.ts

@ -172,7 +172,7 @@ describe("getObjectDiff", () => { @@ -172,7 +172,7 @@ describe("getObjectDiff", () => {
member: true,
hobbies: {
football: ["psg", "nantes"],
rugby: ["france"],
golf: ["st andrews"],
},
},
},
@ -206,7 +206,7 @@ describe("getObjectDiff", () => { @@ -206,7 +206,7 @@ describe("getObjectDiff", () => {
member: true,
hobbies: {
football: ["psg", "nantes"],
rugby: ["france"],
golf: ["st andrews"],
},
},
},
@ -231,7 +231,7 @@ describe("getObjectDiff", () => { @@ -231,7 +231,7 @@ describe("getObjectDiff", () => {
member: true,
hobbies: {
football: ["psg", "nantes"],
rugby: ["france"],
golf: ["st andrews"],
},
},
status: "updated",
@ -250,7 +250,7 @@ describe("getObjectDiff", () => { @@ -250,7 +250,7 @@ describe("getObjectDiff", () => {
},
currentValue: {
football: ["psg", "nantes"],
rugby: ["france"],
golf: ["st andrews"],
},
status: "updated",
subDiff: [
@ -260,11 +260,17 @@ describe("getObjectDiff", () => { @@ -260,11 +260,17 @@ describe("getObjectDiff", () => {
currentValue: ["psg", "nantes"],
status: "updated",
},
{
name: "golf",
previousValue: undefined,
currentValue: ["st andrews"],
status: "added",
},
{
name: "rugby",
previousValue: ["france"],
currentValue: ["france"],
status: "equal",
currentValue: undefined,
status: "deleted",
},
],
},

Loading…
Cancel
Save