Skip to content

Commit

Permalink
fix: ignore key order when comparing objects at max depth
Browse files Browse the repository at this point in the history
  • Loading branch information
marcolink committed Sep 4, 2024
1 parent 13ac156 commit 0c59d87
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 6 deletions.
37 changes: 32 additions & 5 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ describe('a generate json patch function', () => {
},
};

it('detects changes as a given depth of 3', () => {
it('detects changes at a given depth of 3', () => {
const patch = generateJSONPatch(before, after, { maxDepth: 3 });
expect(patch).to.eql([
{
Expand Down Expand Up @@ -737,7 +737,7 @@ describe('a generate json patch function', () => {
expect(patch).to.eql([]);
});

it('detects changes as a given depth of 4', () => {
it('detects changes at a given depth of 4', () => {
const afterModified = structuredClone(after);
afterModified.firstLevel.secondLevel.thirdLevelTwo = 'hello-world';
const patch = generateJSONPatch(before, afterModified, { maxDepth: 4 });
Expand All @@ -757,7 +757,7 @@ describe('a generate json patch function', () => {
]);
});

it('detects changes as a given depth of 4 for an array value', () => {
it('detects changes at a given depth of 4 for an array value', () => {
const afterModified = structuredClone(before);
afterModified.firstLevel.secondLevel.thirdLevelThree = ['test'];
const patch = generateJSONPatch(before, afterModified, { maxDepth: 4 });
Expand All @@ -770,7 +770,7 @@ describe('a generate json patch function', () => {
]);
});

it('detects changes as a given depth of 4 for an removed array value', () => {
it('detects changes as a given depth of 4 for a removed array value', () => {
const afterModified = structuredClone(before);
// @ts-ignore
delete afterModified.firstLevel.secondLevel.thirdLevelThree;
Expand All @@ -783,7 +783,7 @@ describe('a generate json patch function', () => {
]);
});

it('detects changes as a given depth of 4 for an nullyfied array value', () => {
it('detects changes as a given depth of 4 for a nullyfied array value', () => {
const afterModified = structuredClone(before);
// @ts-ignore
afterModified.firstLevel.secondLevel.thirdLevelThree = null;
Expand All @@ -796,6 +796,33 @@ describe('a generate json patch function', () => {
},
]);
});

it('ignores key order on objects when comparing at max depth', () => {
const before = {
a: {
b: {
c: {
d: 'hello',
e: 'world',
},
},
},
};

const after = {
a: {
b: {
c: {
e: 'world',
d: 'hello',
},
},
},
};

const patch = generateJSONPatch(before, after, { maxDepth: 1 });
expect(patch).to.eql([]);
});
});
});

Expand Down
25 changes: 24 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export function generateJSONPatch(
} else if (isJsonObject(rightValue)) {
if (isJsonObject(leftValue)) {
if (maxDepthReached(newPath)) {
if (JSON.stringify(leftValue) !== JSON.stringify(rightValue)) {
if (!deepEqual(leftValue, rightValue)) {
patch.push({ op: 'replace', path: newPath, value: rightValue });
}
} else {
Expand Down Expand Up @@ -251,6 +251,29 @@ function isJsonObject(value: JsonValue): value is JsonObject {
return value?.constructor === Object;
}

function deepEqual(objA: any, objB: any) {
return stringifySorted(objA) === stringifySorted(objB);
}

function stringifySorted(obj: any): string {
if (typeof obj !== 'object' || obj === null) {
return JSON.stringify(obj);
}

if (Array.isArray(obj)) {
return JSON.stringify(obj.map((item) => stringifySorted(item)));
}

const sortedObj: Record<string, string> = {};
const sortedKeys = Object.keys(obj).sort();

sortedKeys.forEach((key) => {
sortedObj[key] = stringifySorted(obj[key]);
});

return JSON.stringify(sortedObj);
}

export type PathInfoResult = {
segments: string[];
length: number;
Expand Down

0 comments on commit 0c59d87

Please sign in to comment.