An open API service indexing awesome lists of open source software.

https://github.com/open-tech-foundation/obj-diff

πŸš€ The Fast, Accurate, JavaScript Objects Diffing & Patching Library.
https://github.com/open-tech-foundation/obj-diff

deep diff javascript library objects patching typescript

Last synced: 3 months ago
JSON representation

πŸš€ The Fast, Accurate, JavaScript Objects Diffing & Patching Library.

Awesome Lists containing this project

README

          

Β [OPEN TECH FOUNDATION](https://open-tech-foundation.pages.dev/)

# obj-diff

[![Build](https://github.com/Open-Tech-Foundation/obj-diff/actions/workflows/build.yml/badge.svg)](https://github.com/Open-Tech-Foundation/obj-diff/actions/workflows/build.yml) Β  [![JSR Score](https://jsr.io/badges/@opentf/obj-diff/score)](https://jsr.io/@opentf/obj-diff)

![Demo image](./demo.png)

> The Fast, Accurate, JavaScript Objects Diffing & Patching Library.

**[LIVE DEMO](https://obj-diff.pages.dev/)**

## Features

- Deep Objects Diffing

- Patching

- Supports comparing custom object types

- TypeScript Support

- Cross-Platform

## Supported Types

- Primitives

- Undefined
- Null
- Number
- String
- Boolean
- BigInt

- Objects
- Plain Objects, eg: `{}`
- Array
- Date
- Map
- Set

## Installation

Install it using your favourite package manager.

```sh
npm install @opentf/obj-diff
```

```sh
yarn add @opentf/obj-diff
```

```sh
pnpm add @opentf/obj-diff
```

```sh
bun add @opentf/obj-diff
```

```sh
deno add @opentf/obj-diff
```

## Usage

```js
import { diff } from '@opentf/obj-diff';

diff(obj1: object, obj2: object): Array
```

```ts
type DiffResult = {
t: 0 | 1 | 2; // The type of diff, 0 - Deleted, 1 - Created, 2 - Updated
p: Array; // The object path
v?: unknown; // The current value
};
```

## Examples

1. Diff two simple objects.

```js
const a = { a: 1, b: 2 };
const b = { a: 2, c: 3 };

diff(a, b);
/*
[
{
t: 2,
p: ["a"],
v: 2,
},
{
t: 0,
p: ["b"],
},
{
t: 1,
p: ["c"],
v: 5,
},
]
*/
```

2. Diff two arrays.

```js
const a = [1, 2, 3, 4, 5];
const b = [1, 3, 5];

diff(a, b);
/*
[
{
t: 2,
p: [1],
v: 3,
},
{
t: 2,
p: [2],
v: 5,
},
{
t: 0,
p: [3],
},
{
t: 0,
p: [4],
},
]
*/
```

3. Deep diff two objects.

```js
const a = {
foo: {
bar: {
a: ["a", "b"],
b: 2,
c: ["x", "y"],
e: 100,
},
},
buzz: "world",
};

const b = {
foo: {
bar: {
a: ["a"],
b: 2,
c: ["x", "y", "z"],
d: "Hello, world!",
},
},
buzz: "fizz",
};

diff(a, b);
/*
[
{
t: 0,
p: ["foo", "bar", "a", 1],
},
{
t: 1,
p: ["foo", "bar", "c", 2],
v: "z",
},
{
t: 0,
p: ["foo", "bar", "e"],
},
{
t: 1,
p: ["foo", "bar", "d"],
v: "Hello, world!",
},
{
t: 2,
p: ["buzz"],
v: "fizz",
},
]
*/
```

## Patching

You can apply the diff result onto the original object to get the modified object.

```js
import { diff, patch } from "@opentf/obj-diff";

const a = { a: 1, b: 2 };
const b = { a: 2, c: 3 };

const out = patch(a, diff(a, b));

assert.deepStrictEqual(out, b); // ok
```

## Comparing Custom Types

By default, the `diff` function cannot compare every object types other than the supported list above.

You can extend the default `diff` function using the `diffWith` function.

Now you can compare any object types of your own.

### Usage - diffWith()

```js
import { diffWith } from '@opentf/obj-diff';

diffWith(
obj1: object,
obj2: object,
fn: (a: object, b: object) => boolean | undefined
): Array
```

### Examples

Let us compare the `MongoDB` bson `ObjectId` objects.

```js
import { ObjectId } from "bson";
import { diffWith } from "@opentf/obj-diff";

const record1 = {
_id: new ObjectId(),
title: "Article 1",
desc: "The article description.",
};

const record2 = {
_id: new ObjectId(),
title: "Article 1",
desc: "The new article description.",
};

const result = diffWith(record1, record2, (a, b) => {
if (a instanceof ObjectId && b instanceof ObjectId) {
return a.toString() !== b.toString();
}
});

console.log(result);
/*
[
{
t: 2,
p: [ "_id" ],
v: new ObjectId('663088b877dd3c9aaec482d4'),
},
{
t: 2,
p: [ "desc" ],
v: "The new article description.",
}
]
*/
```

## FAQs

### 1. **Why the standard JSON Patch protocol is not supported?**

The `JSON Patch` protocol is complicated in nature. And simply we don't want use it as our existing solution works for most of the projects.

### 2. What is the meaning of empty array `{p: []}` in path property?

The empty path denotes `Root` path, and it simply means the entire object was replaced.

For Eg:

```js
diff({}, null); //=> [{t: 2, p: [], v: null}]
```

## Benchmark

```diff
β”Œβ”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚ Task Name β”‚ ops/sec β”‚ Average Time (ns) β”‚ Margin β”‚ Samples β”‚
β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
+ 0 β”‚ diff β”‚ 252,694 β”‚ 3957.346814404028 β”‚ Β±1.60% β”‚ 25270 β”‚
β”‚ 1 β”‚ microdiff β”‚ 218,441 β”‚ 4577.892286564301 β”‚ Β±0.92% β”‚ 21845 β”‚
β”‚ 2 β”‚ deep-object-diff β”‚ 121,385 β”‚ 8238.188318642591 β”‚ Β±1.66% β”‚ 12139 β”‚
β”‚ 3 β”‚ just-diff β”‚ 105,292 β”‚ 9497.35384615396 β”‚ Β±1.66% β”‚ 10530 β”‚
β”‚ 4 β”‚ deep-diff β”‚ 160,802 β”‚ 6218.820533549017 β”‚ Β±1.59% β”‚ 16081 β”‚
β””β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

### Running benchmarks

```sh
$ bun run build
$ bun benchmark.js
```

## Articles

Please read our important articles:

- [Introducing Our New JavaScript Standard Library](https://ganapathy.hashnode.dev/introducing-our-new-javascript-standard-library)

- [You Don’t Need JavaScript Native Methods](https://ganapathy.hashnode.dev/you-dont-need-javascript-native-methods)

## License

Copyright (c) [Thanga Ganapathy](https://github.com/Thanga-Ganapathy) ([MIT License](./LICENSE)).