Browse Source

feat: start deep nested diff

pull/1/head
DoneDeal0 2 years ago
parent
commit
813ef278c1
  1. 1
      src/model.ts
  2. 72
      src/object-diff.ts
  3. 356
      test/object-diff.test.ts

1
src/model.ts

@ -27,6 +27,7 @@ export type Subproperties = { @@ -27,6 +27,7 @@ export type Subproperties = {
previousValue: any;
currentValue: any;
status: DiffStatus;
subDiff?: Subproperties[];
};
export type ObjectDiff = {

72
src/object-diff.ts

@ -58,6 +58,53 @@ function formatSingleObjectDiff( @@ -58,6 +58,53 @@ function formatSingleObjectDiff(
};
}
function getPreviousMatch(
prevSubValues: [string, any][] | null,
nextSubProperty: any
): any | undefined {
const previousMatch =
prevSubValues &&
prevSubValues.find(([subPreviousKey]) =>
isEqual(subPreviousKey, nextSubProperty)
);
return previousMatch ? previousMatch[1] : 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"];
Object.entries(nextValue).forEach(([nextSubProperty, nextSubValue]) => {
const previousMatch = getPreviousMatch(prevSubValues, nextSubProperty);
if (isObject(nextSubValue)) {
const data: Subproperties[] = getSubPropertiesDiff(
previousMatch,
nextSubValue
);
if (data && data.length > 0) {
subDiff = data;
}
}
if (prevSubValues) {
if (previousMatch) {
subPropertiesDiff.push({
name: nextSubProperty,
previousValue: previousMatch,
currentValue: nextSubValue,
status: isEqual(previousMatch, nextSubValue)
? STATUS.EQUAL
: STATUS.UPDATED,
...(!!subDiff && { subDiff }),
});
}
}
});
return subPropertiesDiff;
}
export function getObjectDiff(
prevData: ObjectData,
nextData: ObjectData
@ -80,27 +127,10 @@ export function getObjectDiff( @@ -80,27 +127,10 @@ export function getObjectDiff(
const previousValue = prevData[nextProperty];
if (isObject(nextValue)) {
const prevSubValues = previousValue
? Object.entries(previousValue)
: null;
const subPropertiesDiff: Subproperties[] = [];
Object.entries(nextValue).forEach(([nextSubProperty, nextSubValue]) => {
if (prevSubValues) {
const previousMatch = prevSubValues.find(([subPreviousKey]) =>
isEqual(subPreviousKey, nextSubProperty)
);
if (previousMatch) {
subPropertiesDiff.push({
name: nextSubProperty,
previousValue: previousMatch[1],
currentValue: nextSubValue,
status: isEqual(previousMatch[1], nextSubValue)
? STATUS.EQUAL
: STATUS.UPDATED,
});
}
}
});
const subPropertiesDiff: Subproperties[] = getSubPropertiesDiff(
previousValue,
nextValue
);
const _status = subPropertiesDiff.some(
(property) => property.status !== STATUS.EQUAL
)

356
test/object-diff.test.ts

@ -1,95 +1,215 @@ @@ -1,95 +1,215 @@
import { getObjectDiff } from "../src/object-diff";
describe("getObjectDiff", () => {
it("returns an empty diff if no objects are provided", () => {
expect(getObjectDiff(null, null)).toStrictEqual({
type: "object",
status: "equal",
diff: [],
});
});
it("consider previous object as completely deleted if no next object is provided", () => {
expect(
getObjectDiff(
{ name: "joe", age: 54, hobbies: ["golf", "football"] },
null
// it("returns an empty diff if no objects are provided", () => {
// expect(getObjectDiff(null, null)).toStrictEqual({
// type: "object",
// status: "equal",
// diff: [],
// });
// });
// it("consider previous object as completely deleted if no next object is provided", () => {
// expect(
// getObjectDiff(
// { name: "joe", age: 54, hobbies: ["golf", "football"] },
// null
// )
// ).toStrictEqual({
// type: "object",
// status: "deleted",
// diff: [
// {
// property: "name",
// previousValue: "joe",
// currentValue: undefined,
// status: "deleted",
// },
// {
// property: "age",
// previousValue: 54,
// currentValue: undefined,
// status: "deleted",
// },
// {
// property: "hobbies",
// previousValue: ["golf", "football"],
// currentValue: undefined,
// status: "deleted",
// },
// ],
// });
// });
// it("consider next object as completely added if no previous object is provided", () => {
// expect(
// getObjectDiff(null, {
// name: "joe",
// age: 54,
// hobbies: ["golf", "football"],
// })
// ).toStrictEqual({
// type: "object",
// status: "added",
// diff: [
// {
// property: "name",
// previousValue: undefined,
// currentValue: "joe",
// status: "added",
// },
// {
// property: "age",
// previousValue: undefined,
// currentValue: 54,
// status: "added",
// },
// {
// property: "hobbies",
// previousValue: undefined,
// currentValue: ["golf", "football"],
// status: "added",
// },
// ],
// });
// });
// it("detects changed between two objects", () => {
// expect(
// getObjectDiff(
// {
// id: 54,
// user: {
// name: "joe",
// member: true,
// hobbies: ["golf", "football"],
// age: 66,
// },
// },
// {
// id: 54,
// user: {
// name: "joe",
// member: false,
// hobbies: ["golf", "chess"],
// age: 66,
// },
// }
// )
// ).toStrictEqual({
// type: "object",
// status: "updated",
// diff: [
// {
// property: "id",
// previousValue: 54,
// currentValue: 54,
// status: "equal",
// },
// {
// property: "user",
// previousValue: {
// name: "joe",
// member: true,
// hobbies: ["golf", "football"],
// age: 66,
// },
// currentValue: {
// name: "joe",
// member: false,
// hobbies: ["golf", "chess"],
// age: 66,
// },
// status: "updated",
// subPropertiesDiff: [
// {
// name: "name",
// previousValue: "joe",
// currentValue: "joe",
// status: "equal",
// },
// {
// name: "member",
// previousValue: true,
// currentValue: false,
// status: "updated",
// },
// {
// name: "hobbies",
// previousValue: ["golf", "football"],
// currentValue: ["golf", "chess"],
// status: "updated",
// },
// {
// name: "age",
// previousValue: 66,
// currentValue: 66,
// status: "equal",
// },
// ],
// },
// ],
// });
// });
it("detects changed between two deep nested objects", () => {
console.log(
"res",
JSON.stringify(
getObjectDiff(
{
id: 54,
user: {
name: "joe",
data: {
member: true,
hobbies: {
football: ["psg"],
rugby: ["france"],
},
},
},
},
{
id: 54,
user: {
name: "joe",
data: {
member: true,
hobbies: {
football: ["psg", "nantes"],
rugby: ["france"],
},
},
},
}
),
null,
2
)
).toStrictEqual({
type: "object",
status: "deleted",
diff: [
{
property: "name",
previousValue: "joe",
currentValue: undefined,
status: "deleted",
},
{
property: "age",
previousValue: 54,
currentValue: undefined,
status: "deleted",
},
{
property: "hobbies",
previousValue: ["golf", "football"],
currentValue: undefined,
status: "deleted",
},
],
});
});
it("consider next object as completely added if no previous object is provided", () => {
expect(
getObjectDiff(null, {
name: "joe",
age: 54,
hobbies: ["golf", "football"],
})
).toStrictEqual({
type: "object",
status: "added",
diff: [
{
property: "name",
previousValue: undefined,
currentValue: "joe",
status: "added",
},
{
property: "age",
previousValue: undefined,
currentValue: 54,
status: "added",
},
{
property: "hobbies",
previousValue: undefined,
currentValue: ["golf", "football"],
status: "added",
},
],
});
});
it("detects changed between two objects", () => {
);
expect(
getObjectDiff(
{
id: 54,
user: {
name: "joe",
member: true,
hobbies: ["golf", "football"],
age: 66,
data: {
member: true,
hobbies: {
football: ["psg"],
rugby: ["france"],
},
},
},
},
{
id: 54,
user: {
name: "joe",
member: false,
hobbies: ["golf", "chess"],
age: 66,
data: {
member: true,
hobbies: {
football: ["psg", "nantes"],
rugby: ["france"],
},
},
},
}
)
@ -107,15 +227,23 @@ describe("getObjectDiff", () => { @@ -107,15 +227,23 @@ describe("getObjectDiff", () => {
property: "user",
previousValue: {
name: "joe",
member: true,
hobbies: ["golf", "football"],
age: 66,
data: {
member: true,
hobbies: {
football: ["psg"],
rugby: ["france"],
},
},
},
currentValue: {
name: "joe",
member: false,
hobbies: ["golf", "chess"],
age: 66,
data: {
member: true,
hobbies: {
football: ["psg", "nantes"],
rugby: ["france"],
},
},
},
status: "updated",
subPropertiesDiff: [
@ -126,22 +254,56 @@ describe("getObjectDiff", () => { @@ -126,22 +254,56 @@ describe("getObjectDiff", () => {
status: "equal",
},
{
name: "member",
previousValue: true,
currentValue: false,
name: "data",
previousValue: {
member: true,
hobbies: {
football: ["psg"],
rugby: ["france"],
},
},
currentValue: {
member: true,
hobbies: {
football: ["psg", "nantes"],
rugby: ["france"],
},
},
status: "updated",
},
{
name: "hobbies",
previousValue: ["golf", "football"],
currentValue: ["golf", "chess"],
status: "updated",
},
{
name: "age",
previousValue: 66,
currentValue: 66,
status: "equal",
subDiff: [
{
name: "member",
previousValue: true,
currentValue: true,
status: "equal",
},
{
name: "hobbies",
previousValue: {
football: ["psg"],
rugby: ["france"],
},
currentValue: {
football: ["psg", "nantes"],
rugby: ["france"],
},
status: "updated",
subDiff: [
{
name: "football",
previousValue: ["psg"],
currentValue: ["psg", "nantes"],
status: "updated", // error the algo says it's equal...
},
{
name: "rugby",
previousValue: ["france"],
currentValue: ["france"],
status: "equal",
},
],
},
],
},
],
},

Loading…
Cancel
Save