Skip to content

Commit 55be2fc

Browse files
committed
chore: improve getobjectdiff performance
1 parent 82780cc commit 55be2fc

File tree

2 files changed

+131
-153
lines changed

2 files changed

+131
-153
lines changed

src/lib/object-diff/index.ts

Lines changed: 91 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,19 @@ function getLeanDiff(
1414
showOnly: ObjectDiffOptions["showOnly"] = DEFAULT_OBJECT_DIFF_OPTIONS.showOnly,
1515
): ObjectDiff["diff"] {
1616
const { statuses, granularity } = showOnly;
17-
return diff.reduce(
18-
(acc, value) => {
19-
if (granularity === GRANULARITY.DEEP && value.diff) {
20-
const leanDiff = getLeanDiff(value.diff, showOnly);
21-
if (leanDiff.length > 0) {
22-
return [...acc, { ...value, diff: leanDiff }];
23-
}
17+
const res: ObjectDiff["diff"] = [];
18+
for (let i = 0; i < diff.length; i++) {
19+
const value = diff[i];
20+
if (granularity === GRANULARITY.DEEP && value.diff) {
21+
const leanDiff = getLeanDiff(value.diff, showOnly);
22+
if (leanDiff.length > 0) {
23+
res.push({ ...value, diff: leanDiff });
2424
}
25-
if (statuses.includes(value.status)) {
26-
return [...acc, value];
27-
}
28-
return acc;
29-
},
30-
[] as ObjectDiff["diff"],
31-
);
25+
} else if (statuses.includes(value.status)) {
26+
res.push(value);
27+
}
28+
}
29+
return res;
3230
}
3331

3432
function getObjectStatus(diff: ObjectDiff["diff"]): OBJECT_STATUS {
@@ -50,34 +48,37 @@ function formatSingleObjectDiff(
5048
};
5149
}
5250
const diff: ObjectDiff["diff"] = [];
53-
Object.entries(data).forEach(([property, value]) => {
51+
52+
for (const [property, value] of Object.entries(data)) {
5453
if (isObject(value)) {
5554
const subPropertiesDiff: Diff[] = [];
56-
Object.entries(value).forEach(([subProperty, subValue]) => {
55+
for (const [subProperty, subValue] of Object.entries(value)) {
5756
subPropertiesDiff.push({
5857
property: subProperty,
5958
previousValue: status === OBJECT_STATUS.ADDED ? undefined : subValue,
6059
currentValue: status === OBJECT_STATUS.ADDED ? subValue : undefined,
6160
status,
6261
});
63-
});
64-
return diff.push({
62+
}
63+
diff.push({
6564
property,
6665
previousValue:
6766
status === OBJECT_STATUS.ADDED ? undefined : data[property],
6867
currentValue: status === OBJECT_STATUS.ADDED ? value : undefined,
6968
status,
7069
diff: subPropertiesDiff,
7170
});
71+
} else {
72+
diff.push({
73+
property,
74+
previousValue:
75+
status === OBJECT_STATUS.ADDED ? undefined : data[property],
76+
currentValue: status === OBJECT_STATUS.ADDED ? value : undefined,
77+
status,
78+
});
7279
}
73-
return diff.push({
74-
property,
75-
previousValue:
76-
status === OBJECT_STATUS.ADDED ? undefined : data[property],
77-
currentValue: status === OBJECT_STATUS.ADDED ? value : undefined,
78-
status,
79-
});
80-
});
80+
}
81+
8182
if (options.showOnly && options.showOnly.statuses.length > 0) {
8283
return {
8384
type: "object",
@@ -92,20 +93,6 @@ function formatSingleObjectDiff(
9293
};
9394
}
9495

95-
function getPreviousMatch(
96-
previousValue: unknown | undefined,
97-
nextSubProperty: unknown,
98-
options?: ObjectDiffOptions,
99-
): unknown | undefined {
100-
if (!previousValue) {
101-
return undefined;
102-
}
103-
const previousMatch = Object.entries(previousValue).find(([subPreviousKey]) =>
104-
isEqual(subPreviousKey, nextSubProperty, options),
105-
);
106-
return previousMatch ? previousMatch[1] : undefined;
107-
}
108-
10996
function getValueStatus(
11097
previousValue: unknown,
11198
nextValue: unknown,
@@ -128,80 +115,70 @@ function getPropertyStatus(subPropertiesDiff: Diff[]): OBJECT_STATUS {
128115
function getDeletedProperties(
129116
previousValue: Record<string, unknown> | undefined,
130117
nextValue: Record<string, unknown>,
131-
): { property: string; value: unknown }[] | undefined {
132-
if (!previousValue) return undefined;
133-
const prevKeys = Object.keys(previousValue);
134-
const nextKeys = Object.keys(nextValue);
135-
const deletedKeys = prevKeys.filter((prevKey) => !nextKeys.includes(prevKey));
136-
if (deletedKeys.length > 0) {
137-
return deletedKeys.map((deletedKey) => ({
138-
property: deletedKey,
139-
value: previousValue[deletedKey],
140-
}));
118+
): { property: string; value: unknown }[] {
119+
const res: { property: string; value: unknown }[] = [];
120+
if (!previousValue) return res;
121+
for (const [property, value] of Object.entries(previousValue)) {
122+
if (!(property in nextValue)) {
123+
res.push({ property, value });
124+
}
141125
}
142-
return undefined;
126+
return res;
143127
}
144128

145129
function getSubPropertiesDiff(
146-
previousValue: Record<string, unknown> | undefined,
130+
previousValue: Record<string, unknown> | undefined = {},
147131
nextValue: Record<string, unknown>,
148132
options?: ObjectDiffOptions,
149133
): Diff[] {
150134
const subPropertiesDiff: Diff[] = [];
151-
let subDiff: Diff[];
152-
const deletedMainSubProperties = getDeletedProperties(
153-
previousValue,
154-
nextValue,
155-
);
156-
if (deletedMainSubProperties) {
157-
deletedMainSubProperties.forEach((deletedProperty) => {
135+
const allKeys = new Set([
136+
...Object.keys(previousValue),
137+
...Object.keys(nextValue),
138+
]);
139+
140+
for (const property of allKeys) {
141+
const prevSubValue = previousValue[property];
142+
const nextSubValue = nextValue[property];
143+
if (!(property in nextValue)) {
158144
subPropertiesDiff.push({
159-
property: deletedProperty.property,
160-
previousValue: deletedProperty.value,
145+
property,
146+
previousValue: prevSubValue,
161147
currentValue: undefined,
162148
status: OBJECT_STATUS.DELETED,
163149
});
164-
});
165-
}
166-
Object.entries(nextValue).forEach(([nextSubProperty, nextSubValue]) => {
167-
const previousMatch = getPreviousMatch(
168-
previousValue,
169-
nextSubProperty,
170-
options,
171-
);
172-
if (!previousMatch) {
173-
return subPropertiesDiff.push({
174-
property: nextSubProperty,
175-
previousValue: previousMatch,
150+
continue;
151+
}
152+
if (!(property in previousValue)) {
153+
subPropertiesDiff.push({
154+
property,
155+
previousValue: undefined,
176156
currentValue: nextSubValue,
177-
status:
178-
!previousValue || !(nextSubProperty in previousValue)
179-
? OBJECT_STATUS.ADDED
180-
: previousMatch === nextSubValue
181-
? OBJECT_STATUS.EQUAL
182-
: OBJECT_STATUS.UPDATED,
157+
status: OBJECT_STATUS.ADDED,
183158
});
159+
continue;
184160
}
185-
if (isObject(nextSubValue)) {
186-
const data: Diff[] = getSubPropertiesDiff(
187-
previousMatch as Record<string, unknown>,
188-
nextSubValue,
189-
options,
190-
);
191-
if (data && data.length > 0) {
192-
subDiff = data;
193-
}
194-
}
195-
if (previousMatch) {
161+
if (isObject(nextSubValue) && isObject(prevSubValue)) {
162+
const subDiff = getSubPropertiesDiff(prevSubValue, nextSubValue, options);
163+
const status =
164+
subDiff.length > 0 ? OBJECT_STATUS.UPDATED : OBJECT_STATUS.EQUAL;
165+
subPropertiesDiff.push({
166+
property,
167+
previousValue: prevSubValue,
168+
currentValue: nextSubValue,
169+
status,
170+
...(status !== OBJECT_STATUS.EQUAL && { diff: subDiff }),
171+
});
172+
} else {
173+
const status = getValueStatus(prevSubValue, nextSubValue, options);
196174
subPropertiesDiff.push({
197-
property: nextSubProperty,
198-
previousValue: previousMatch,
175+
property,
176+
previousValue: prevSubValue,
199177
currentValue: nextSubValue,
200-
status: getValueStatus(previousMatch, nextSubValue, options),
201-
...(!!subDiff && { diff: subDiff }),
178+
status,
202179
});
203180
}
204-
});
181+
}
205182
return subPropertiesDiff;
206183
}
207184

@@ -235,10 +212,10 @@ export function getObjectDiff(
235212
return formatSingleObjectDiff(prevData, OBJECT_STATUS.DELETED, options);
236213
}
237214
const diff: ObjectDiff["diff"] = [];
238-
Object.entries(nextData).forEach(([nextProperty, nextValue]) => {
215+
for (const [nextProperty, nextValue] of Object.entries(nextData)) {
239216
const previousValue = prevData[nextProperty];
240217
if (!previousValue) {
241-
return diff.push({
218+
diff.push({
242219
property: nextProperty,
243220
previousValue,
244221
currentValue: nextValue,
@@ -248,15 +225,14 @@ export function getObjectDiff(
248225
? OBJECT_STATUS.EQUAL
249226
: OBJECT_STATUS.UPDATED,
250227
});
251-
}
252-
if (isObject(nextValue)) {
228+
} else if (isObject(nextValue)) {
253229
const subPropertiesDiff: Diff[] = getSubPropertiesDiff(
254230
previousValue as Record<string, unknown>,
255231
nextValue,
256232
options,
257233
);
258234
const subPropertyStatus = getPropertyStatus(subPropertiesDiff);
259-
return diff.push({
235+
diff.push({
260236
property: nextProperty,
261237
previousValue,
262238
currentValue: nextValue,
@@ -265,23 +241,23 @@ export function getObjectDiff(
265241
diff: subPropertiesDiff,
266242
}),
267243
});
268-
}
269-
return diff.push({
270-
property: nextProperty,
271-
previousValue,
272-
currentValue: nextValue,
273-
status: getValueStatus(previousValue, nextValue, options),
274-
});
275-
});
276-
const deletedProperties = getDeletedProperties(prevData, nextData);
277-
if (deletedProperties) {
278-
deletedProperties.forEach((deletedProperty) => {
244+
} else {
279245
diff.push({
280-
property: deletedProperty.property,
281-
previousValue: deletedProperty.value,
282-
currentValue: undefined,
283-
status: OBJECT_STATUS.DELETED,
246+
property: nextProperty,
247+
previousValue,
248+
currentValue: nextValue,
249+
status: getValueStatus(previousValue, nextValue, options),
284250
});
251+
}
252+
}
253+
const deletedProperties = getDeletedProperties(prevData, nextData);
254+
for (let i = 0; i < deletedProperties.length; i++) {
255+
const deletedProperty = deletedProperties[i];
256+
diff.push({
257+
property: deletedProperty.property,
258+
previousValue: deletedProperty.value,
259+
currentValue: undefined,
260+
status: OBJECT_STATUS.DELETED,
285261
});
286262
}
287263
if (options.showOnly && options.showOnly.statuses.length > 0) {

0 commit comments

Comments
 (0)