Skip to content

Commit 9f32cad

Browse files
authored
Merge pull request #10 from DoneDeal0/granular-output
feat: add granular output
2 parents 4362c98 + 9a1ca68 commit 9f32cad

13 files changed

+1458
-641
lines changed

README.md

Lines changed: 75 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,10 @@ This library compares two arrays or objects and return a complete diff of their
66

77
## WHY YOU SHOULD USE THIS LIB
88

9-
All other existing solutions return a weird diff format which often require an additional parsing. They are also slow and limited to object comparison. 👎
9+
All other existing solutions return a weird diff format which often require an additional parsing. They are also limited to object comparison. 👎
1010

1111
**Superdiff** gives you a complete diff for both array <u>and</u> objects with a very readable format. Last but not least, it's battled tested and super fast. Import. Enjoy. 👍
1212

13-
**Benchmark**:
14-
15-
| Objects | Deep-diff 🐢 | Superdiff ⚡ |
16-
| --------- | ------------ | ------------ |
17-
| 1.000 | 10.47ms | 5.73ms |
18-
| 10.000 | 43.05ms | 18.60ms |
19-
| 100.000 | 289.71ms | 50.96ms |
20-
| 1.000.000 | 2786.70ms | 389.78ms |
21-
2213
## DIFF FORMAT COMPARISON
2314

2415
Let's compare the diff format of **Superdiff** and **Deep-diff**, the most popular diff lib on npm:
@@ -51,20 +42,19 @@ const objectB = {
5142

5243
```js
5344
[
54-
DiffEdit {
55-
kind: 'E',
56-
path: [ 'user', 'member' ],
57-
lhs: true,
58-
rhs: false
59-
},
60-
DiffEdit {
61-
kind: 'E',
62-
path: [ 'user', 'hobbies', 1 ],
63-
lhs: 'football',
64-
rhs: 'chess'
65-
}
66-
]
67-
45+
{
46+
kind: "E",
47+
path: ["user", "member"],
48+
lhs: true,
49+
rhs: false,
50+
},
51+
{
52+
kind: "E",
53+
path: ["user", "hobbies", 1],
54+
lhs: "football",
55+
rhs: "chess",
56+
},
57+
];
6858
```
6959

7060
**SuperDiff** output:
@@ -97,25 +87,25 @@ const objectB = {
9787
+ status: "updated",
9888
subPropertiesDiff: [
9989
{
100-
name: "name",
90+
property: "name",
10191
previousValue: "joe",
10292
currentValue: "joe",
10393
status: "equal",
10494
},
10595
+ {
106-
+ name: "member",
96+
+ property: "member",
10797
+ previousValue: true,
10898
+ currentValue: false,
10999
+ status: "updated",
110100
+ },
111101
+ {
112-
+ name: "hobbies",
102+
+ property: "hobbies",
113103
+ previousValue: ["golf", "football"],
114104
+ currentValue: ["golf", "chess"],
115105
+ status: "updated",
116106
+ },
117107
{
118-
name: "age",
108+
property: "age",
119109
previousValue: 66,
120110
currentValue: 66,
121111
status: "equal",
@@ -148,25 +138,47 @@ format:
148138
```ts
149139
type ObjectDiff = {
150140
type: "object";
151-
status: "added" | "deleted" | "equal" | "moved" | "updated";
141+
status: "added" | "deleted" | "equal" | "updated";
152142
diff: {
153143
property: string;
154144
previousValue: any;
155145
currentValue: any;
156-
status: "added" | "deleted" | "equal" | "moved" | "updated";
146+
status: "added" | "deleted" | "equal" | "updated";
157147
// only appears if some subproperties have been added/deleted/updated
158148
subPropertiesDiff?: {
159-
name: string;
149+
property: string;
160150
previousValue: any;
161151
currentValue: any;
162-
status: "added" | "deleted" | "equal" | "moved" | "updated";
152+
status: "added" | "deleted" | "equal" | "updated";
163153
// subDiff is a recursive diff in case of nested subproperties
164-
subDiff?: Subproperties[];
154+
subDiff?: SubProperties[];
165155
}[];
166156
}[];
167157
};
168158
```
169159

160+
**Options**
161+
162+
You can add a third `options` parameter to `getObjectDiff`.
163+
164+
```ts
165+
{
166+
ignoreArrayOrder?: boolean // false by default,
167+
showOnly?: {
168+
statuses: ("added" | "deleted" | "updated" | "equal")[], // [] by default
169+
granularity?: "basic" | "deep" // "basic" by default
170+
}
171+
}
172+
```
173+
174+
- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be considered as `equal`, because the two arrays have the same value, just not in the same order.
175+
- `showOnly`: only returns the values whose status interest you. It has two parameters:
176+
177+
- `statuses`: status you want to see in the output (ex: `["added", "equal"]`)
178+
- `granularity`:
179+
- `basic` only returns the main properties whose status match your request.
180+
- `deep` can return main properties if some of their subproperties' status match your request. The subproperties will be filtered accordingly.
181+
170182
### getListDiff()
171183

172184
```js
@@ -197,6 +209,18 @@ type ListDiff = {
197209
};
198210
```
199211

212+
**Options**
213+
214+
You can add a third `options` parameter to `getListDiff`.
215+
216+
```ts
217+
{
218+
showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
219+
}
220+
```
221+
222+
- `showOnly` gives you the option to only return the values whose status interest you (ex: `["added", "equal"]`).
223+
200224
### isEqual()
201225

202226
```js
@@ -205,6 +229,18 @@ import { isEqual } from "@donedeal0/superdiff";
205229

206230
Checks if two values are equal.
207231

232+
**Options**
233+
234+
You can add a third `options` parameter to `isEqual`.
235+
236+
```ts
237+
{
238+
ignoreArrayOrder?: boolean // false by default,
239+
}
240+
```
241+
242+
- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be considered as `equal`, because the two arrays have the same value, just not in the same order.
243+
208244
### isObject()
209245

210246
```js
@@ -329,25 +365,25 @@ output
329365
+ status: "updated",
330366
subPropertiesDiff: [
331367
{
332-
name: "name",
368+
property: "name",
333369
previousValue: "joe",
334370
currentValue: "joe",
335371
status: "equal",
336372
},
337373
+ {
338-
+ name: "member",
374+
+ property: "member",
339375
+ previousValue: true,
340376
+ currentValue: false,
341377
+ status: "updated",
342378
+ },
343379
+ {
344-
+ name: "hobbies",
380+
+ property: "hobbies",
345381
+ previousValue: ["golf", "football"],
346382
+ currentValue: ["golf", "chess"],
347383
+ status: "updated",
348384
+ },
349385
{
350-
name: "age",
386+
property: "age",
351387
previousValue: 66,
352388
currentValue: 66,
353389
status: "equal",
@@ -393,29 +429,17 @@ output
393429
false;
394430
```
395431

396-
More examples are availble in the tests of the source code.
432+
More examples are available in the tests of the source code.
397433

398434
<hr/>
399435

400-
### OPTIONS
401-
402-
`getObjectDiff()` and `isEqual()` accept a facultative `options` parameter:
403-
404-
```ts
405-
{
406-
discardArrayOrder?: boolean // false by default
407-
}
408-
```
409-
410-
If `discardArrayOrder` is set to `true`, `["hello", "world"]` and `["world", "hello"]` will be considered as `equal`, because the two arrays have the same value, just not in the same order.
411-
412436
## CREDITS
413437

414438
DoneDeal0
415439

416440
## SUPPORT
417441

418-
If you use Superdiff, please show your support by buying me coffee:
442+
If you or your company use Superdiff, please show your support by buying me a coffee:
419443
https://www.buymeacoffee.com/donedeal0
420444

421445
<br/>

dist/index.d.ts

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,69 @@
1-
type DiffStatus = "added" | "equal" | "moved" | "deleted" | "updated";
1+
declare const GRANULARITY: Record<string, "basic" | "deep">;
2+
type ListDiffStatus = "added" | "equal" | "moved" | "deleted" | "updated";
3+
type ObjectDiffStatus = "added" | "equal" | "deleted" | "updated";
24
type ObjectData = Record<string, any> | undefined | null;
35
type ListData = any;
4-
type Options = {
5-
discardArrayOrder?: boolean;
6+
type ObjectStatusTuple = readonly [
7+
"added",
8+
"equal",
9+
"deleted",
10+
"updated"
11+
];
12+
type ListStatusTuple = readonly [
13+
"added",
14+
"equal",
15+
"deleted",
16+
"moved",
17+
"updated"
18+
];
19+
type isEqualOptions = {
20+
ignoreArrayOrder?: boolean;
21+
};
22+
type ObjectOptions = {
23+
ignoreArrayOrder?: boolean;
24+
showOnly?: {
25+
statuses: Array<ObjectStatusTuple[number]>;
26+
granularity?: typeof GRANULARITY[keyof typeof GRANULARITY];
27+
};
28+
};
29+
type ListOptions = {
30+
showOnly?: Array<ListStatusTuple[number]>;
631
};
732
type ListDiff = {
833
type: "list";
9-
status: DiffStatus;
34+
status: ListDiffStatus;
1035
diff: {
1136
value: ListData;
1237
prevIndex: number | null;
1338
newIndex: number | null;
1439
indexDiff: number | null;
15-
status: DiffStatus;
40+
status: ListDiffStatus;
1641
}[];
1742
};
18-
type Subproperties = {
19-
name: string;
43+
type SubProperties = {
44+
property: string;
2045
previousValue: any;
2146
currentValue: any;
22-
status: DiffStatus;
23-
subDiff?: Subproperties[];
47+
status: ObjectDiffStatus;
48+
subPropertiesDiff?: SubProperties[];
2449
};
2550
type ObjectDiff = {
2651
type: "object";
27-
status: DiffStatus;
52+
status: ObjectDiffStatus;
2853
diff: {
2954
property: string;
3055
previousValue: any;
3156
currentValue: any;
32-
status: DiffStatus;
33-
subPropertiesDiff?: Subproperties[];
57+
status: ObjectDiffStatus;
58+
subPropertiesDiff?: SubProperties[];
3459
}[];
3560
};
3661

37-
declare function getObjectDiff(prevData: ObjectData, nextData: ObjectData, options?: Options): ObjectDiff;
62+
declare function getObjectDiff(prevData: ObjectData, nextData: ObjectData, options?: ObjectOptions): ObjectDiff;
3863

39-
declare const getListDiff: (prevList: ListData[] | undefined | null, nextList: ListData[] | undefined | null) => ListDiff;
64+
declare const getListDiff: (prevList: ListData[] | undefined | null, nextList: ListData[] | undefined | null, options?: ListOptions) => ListDiff;
4065

41-
declare function isEqual(a: any, b: any, options?: Options): boolean;
66+
declare function isEqual(a: any, b: any, options?: isEqualOptions): boolean;
4267
declare function isObject(value: any): value is Record<string, any>;
4368

4469
export { getListDiff, getObjectDiff, isEqual, isObject };

dist/index.js

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)