Superdiff provides a complete and readable diff for both arrays and objects. Plus, it supports stream and file inputs for handling large datasets efficiently, is battle-tested, has zero dependencies, and is super fast.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

473 lines
11 KiB

<img width="722" alt="superdiff-logo" src="https://user-images.githubusercontent.com/43271780/209532864-24d7449e-1185-4810-9423-be5df1fe877f.png">
2 years ago
# SUPERDIFF
2 years ago
This library compares two arrays or objects and returns a full diff of their differences.
2 years ago
[![Superdiff CI](https://github.com/DoneDeal0/superdiff/actions/workflows/superdiff.yml/badge.svg)](https://github.com/DoneDeal0/superdiff/actions/workflows/superdiff.yml)
![NPM Downloads](https://img.shields.io/npm/dy/%40donedeal0%2Fsuperdiff?logo=npm)
![GitHub Tag](https://img.shields.io/github/v/tag/DoneDeal0/superdiff?label=latest%20release)
## WHY YOU SHOULD USE THIS LIBRARY
2 years ago
All other existing solutions return a strange diff format that often requires additional parsing. They are also limited to object comparison. 👎
2 years ago
**Superdiff** gives you a complete diff for both array <u>and</u> objects in a very readable format. Last but not least, it's battle-tested and super fast. Import. Enjoy. 👍
## DONORS
I am grateful to the generous donors of **Superdiff**!
<div style="display: flex;>
<a href="https://github.com/AlexisAnzieu" target="_blank"><img alt="AlexisAnzieu" src="https://github.com/DoneDeal0/superdiff/assets/43271780/8e9fb627-36ec-479d-87d4-3ca2cb2a796c" width="72px" height="72px"/></a>
<a href="https://github.com/omonk" target="_blank"><img alt="omonk" src="https://github.com/DoneDeal0/superdiff/assets/43271780/6c040ab4-f6eb-49bf-a737-d138264abbd7" width="72px" height="72px"/></a>
<a href="https://github.com/sneko" target="_blank"><img alt="sneko" src="https://github.com/DoneDeal0/superdiff/assets/43271780/2caaa70b-9586-44d6-8b3a-3755bba7b1ca" width="72px" height="72px"/></a>
</div>
2 years ago
## DIFF FORMAT COMPARISON
Let's compare the diff format of **Superdiff** and **Deep-diff**, the most popular diff lib on npm:
input:
2 years ago
2 years ago
```diff
2 years ago
const objectA = {
id: 54,
user: {
name: "joe",
- member: true,
- hobbies: ["golf", "football"],
age: 66,
},
}
const objectB = {
id: 54,
user: {
name: "joe",
+ member: false,
+ hobbies: ["golf", "chess"],
age: 66,
},
2 years ago
}
```
2 years ago
**Deep-Diff** output:
```js
[
2 years ago
{
kind: "E",
path: ["user", "member"],
lhs: true,
rhs: false,
},
{
kind: "E",
path: ["user", "hobbies", 1],
lhs: "football",
rhs: "chess",
},
];
2 years ago
```
2 years ago
**SuperDiff** output:
2 years ago
2 years ago
```diff
2 years ago
{
type: "object",
2 years ago
+ status: "updated",
2 years ago
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,
},
2 years ago
+ status: "updated",
2 years ago
subPropertiesDiff: [
{
property: "name",
2 years ago
previousValue: "joe",
currentValue: "joe",
status: "equal",
},
2 years ago
+ {
+ property: "member",
2 years ago
+ previousValue: true,
+ currentValue: false,
+ status: "updated",
+ },
+ {
+ property: "hobbies",
2 years ago
+ previousValue: ["golf", "football"],
+ currentValue: ["golf", "chess"],
+ status: "updated",
+ },
2 years ago
{
property: "age",
2 years ago
previousValue: 66,
currentValue: 66,
status: "equal",
},
],
},
],
}
```
2 years ago
## FEATURES
**Superdiff** exports 4 functions:
### getObjectDiff()
2 years ago
```js
2 years ago
import { getObjectDiff } from "@donedeal0/superdiff";
2 years ago
```
Compares two objects and return a diff for each value and their potential subvalues:
2 years ago
- property name
2 years ago
- status: `added`, `deleted`, `equal`, `updated`
2 years ago
- previous value, current value
- supports deeply nested objects with any kind of values
format:
```ts
2 years ago
type ObjectDiff = {
type: "object";
2 years ago
status: "added" | "deleted" | "equal" | "updated";
2 years ago
diff: {
property: string;
previousValue: any;
currentValue: any;
2 years ago
status: "added" | "deleted" | "equal" | "updated";
// only appears if some subproperties have been added/deleted/updated
2 years ago
subPropertiesDiff?: {
property: string;
2 years ago
previousValue: any;
currentValue: any;
2 years ago
status: "added" | "deleted" | "equal" | "updated";
2 years ago
// subDiff is a recursive diff in case of nested subproperties
subDiff?: SubProperties[];
2 years ago
}[];
2 years ago
}[];
2 years ago
};
2 years ago
```
**Options**
2 years ago
You can add a third `options` parameter to `getObjectDiff`.
```ts
{
ignoreArrayOrder?: boolean // false by default,
showOnly?: {
statuses: ("added" | "deleted" | "updated" | "equal")[], // [] by default
2 years ago
granularity?: "basic" | "deep" // "basic" by default
}
}
```
- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
- `showOnly`: returns only the values whose status you are interested in. It takes two parameters:
- `statuses`: status you want to see in the output (e.g. `["added", "equal"]`)
- `granularity`:
- `basic` returns only the main properties whose status matches your query.
- `deep` can return main properties if some of their subproperties' status match your request. The subproperties are filtered accordingly.
2 years ago
### getListDiff()
2 years ago
```js
2 years ago
import { getListDiff } from "@donedeal0/superdiff";
2 years ago
```
2 years ago
Compares two arrays and returns a diff for each value:
2 years ago
- index change: `prevIndex`, `newIndex`, `indexDiff`
- status: `added`, `deleted`, `equal`, `moved`, `updated`
- value
- supports arrays of primitive values and objects
- supports arrays with duplicate values
2 years ago
format:
```ts
2 years ago
type ListDiff = {
type: "list";
2 years ago
status: "added" | "deleted" | "equal" | "moved" | "updated";
2 years ago
diff: {
value: any;
prevIndex: number | null;
newIndex: number | null;
indexDiff: number | null;
2 years ago
status: "added" | "deleted" | "equal" | "moved" | "updated";
2 years ago
}[];
};
```
2 years ago
**Options**
2 years ago
You can add a third `options` parameter to `getListDiff`.
```ts
{
showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
referenceProperty?: string; // "" by default
ignoreArrayOrder?: boolean // false by default,
}
```
- `showOnly` gives you the option to return only the values whose status you are interested in (e.g. `["added", "equal"]`).
- `referenceProperty` will consider an object to be updated instead of added or deleted if one of its properties remains stable, such as its `id`. This option has no effect on other datatypes.
- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
2 years ago
### isEqual()
2 years ago
2 years ago
```js
2 years ago
import { isEqual } from "@donedeal0/superdiff";
2 years ago
```
Tests whether two values are equal.
2 years ago
**Options**
2 years ago
You can add a third `options` parameter to `isEqual`.
```ts
{
ignoreArrayOrder?: boolean // false by default,
}
```
- `ignoreArrayOrder`: if set to `true`, `["hello", "world"]` and `["world", "hello"]` will be treated as `equal`, because the two arrays have the same value, just not in the same order.
2 years ago
### isObject()
2 years ago
```js
2 years ago
import { isObject } from "@donedeal0/superdiff";
2 years ago
```
Tests whether a value is an object.
2 years ago
## EXAMPLES
### getListDiff()
input
2 years ago
2 years ago
```diff
2 years ago
getListDiff(
2 years ago
- ["mbappe", "mendes", "verratti", "ruiz"],
+ ["mbappe", "messi", "ruiz"]
2 years ago
);
```
2 years ago
output
2 years ago
2 years ago
```diff
2 years ago
{
type: "list",
2 years ago
+ status: "updated",
2 years ago
diff: [
{
value: "mbappe",
prevIndex: 0,
newIndex: 0,
indexDiff: 0,
status: "equal",
},
2 years ago
- {
- value: "mendes",
- prevIndex: 1,
- newIndex: null,
- indexDiff: null,
- status: "deleted",
- },
- {
- value: "verratti",
- prevIndex: 2,
- newIndex: null,
- indexDiff: null,
- status: "deleted",
- },
+ {
+ value: "messi",
+ prevIndex: null,
+ newIndex: 1,
+ indexDiff: null,
+ status: "added",
+ },
+ {
+ value: "ruiz",
+ prevIndex: 3,
+ newIndex: 2,
+ indexDiff: -1,
+ status: "moved",
2 years ago
},
],
}
```
2 years ago
### getObjectDiff()
input
```diff
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,
},
}
);
```
output
```diff
{
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: [
{
property: "name",
2 years ago
previousValue: "joe",
currentValue: "joe",
status: "equal",
},
+ {
+ property: "member",
2 years ago
+ previousValue: true,
+ currentValue: false,
+ status: "updated",
+ },
+ {
+ property: "hobbies",
2 years ago
+ previousValue: ["golf", "football"],
+ currentValue: ["golf", "chess"],
+ status: "updated",
+ },
{
property: "age",
2 years ago
previousValue: 66,
currentValue: 66,
status: "equal",
},
],
},
],
}
```
### isEqual()
```js
isEqual(
[
{ name: "joe", age: 99 },
{ name: "nina", age: 23 },
],
[
{ name: "joe", age: 98 },
{ name: "nina", age: 23 },
]
);
```
output
```js
false;
```
### isObject()
input
```js
isObject(["hello", "world"]);
```
output
```js
false;
```
More examples are available in the source code tests.
2 years ago
<hr/>
## CREDITS
2 years ago
DoneDeal0
## SUPPORT
If you or your company uses **Superdiff**, please show your support by becoming a sponsor! Your name and company logo will be displayed on the `README.md`. https://github.com/sponsors/DoneDeal0
<br/>
<a href="https://github.com/sponsors/DoneDeal0" target="_blank">
<img alt="sponsor" src="https://github.com/DoneDeal0/superdiff/assets/43271780/21deb4f3-fee3-4bf9-a945-ed0b77c6f82f"/>
</a>
<br/>
## CONTRIBUTING
2 years ago
Pull requests are welcome!