Skip to content

Commit 0c59d87

Browse files
committed
fix: ignore key order when comparing objects at max depth
1 parent 13ac156 commit 0c59d87

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

src/index.spec.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ describe('a generate json patch function', () => {
679679
},
680680
};
681681

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

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

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

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

786-
it('detects changes as a given depth of 4 for an nullyfied array value', () => {
786+
it('detects changes as a given depth of 4 for a nullyfied array value', () => {
787787
const afterModified = structuredClone(before);
788788
// @ts-ignore
789789
afterModified.firstLevel.secondLevel.thirdLevelThree = null;
@@ -796,6 +796,33 @@ describe('a generate json patch function', () => {
796796
},
797797
]);
798798
});
799+
800+
it('ignores key order on objects when comparing at max depth', () => {
801+
const before = {
802+
a: {
803+
b: {
804+
c: {
805+
d: 'hello',
806+
e: 'world',
807+
},
808+
},
809+
},
810+
};
811+
812+
const after = {
813+
a: {
814+
b: {
815+
c: {
816+
e: 'world',
817+
d: 'hello',
818+
},
819+
},
820+
},
821+
};
822+
823+
const patch = generateJSONPatch(before, after, { maxDepth: 1 });
824+
expect(patch).to.eql([]);
825+
});
799826
});
800827
});
801828

src/index.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ export function generateJSONPatch(
198198
} else if (isJsonObject(rightValue)) {
199199
if (isJsonObject(leftValue)) {
200200
if (maxDepthReached(newPath)) {
201-
if (JSON.stringify(leftValue) !== JSON.stringify(rightValue)) {
201+
if (!deepEqual(leftValue, rightValue)) {
202202
patch.push({ op: 'replace', path: newPath, value: rightValue });
203203
}
204204
} else {
@@ -251,6 +251,29 @@ function isJsonObject(value: JsonValue): value is JsonObject {
251251
return value?.constructor === Object;
252252
}
253253

254+
function deepEqual(objA: any, objB: any) {
255+
return stringifySorted(objA) === stringifySorted(objB);
256+
}
257+
258+
function stringifySorted(obj: any): string {
259+
if (typeof obj !== 'object' || obj === null) {
260+
return JSON.stringify(obj);
261+
}
262+
263+
if (Array.isArray(obj)) {
264+
return JSON.stringify(obj.map((item) => stringifySorted(item)));
265+
}
266+
267+
const sortedObj: Record<string, string> = {};
268+
const sortedKeys = Object.keys(obj).sort();
269+
270+
sortedKeys.forEach((key) => {
271+
sortedObj[key] = stringifySorted(obj[key]);
272+
});
273+
274+
return JSON.stringify(sortedObj);
275+
}
276+
254277
export type PathInfoResult = {
255278
segments: string[];
256279
length: number;

0 commit comments

Comments
 (0)