Skip to content

Commit 84d218f

Browse files
committed
first commit
0 parents  commit 84d218f

File tree

9 files changed

+218
-0
lines changed

9 files changed

+218
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pnpm-lock.yaml
2+
node_modules
3+
dist

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 AsyncBanana
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Microdiff
2+
3+
Microdiff is a tiny (currently <1kb), fast, zero dependency object and array comparison library. It is significantly faster than most other deep comparison libraries, and has full TypeScript support.
4+
5+
## Get started
6+
7+
First, install Microdiff
8+
9+
```
10+
npm i microdiff
11+
```
12+
13+
Then, simply import it and run it on two objects.
14+
15+
```js
16+
import diff from "microdiff";
17+
18+
const obj1 = {
19+
originalProperty: true,
20+
};
21+
const obj2 = {
22+
originalProperty: true,
23+
newProperty: "new",
24+
};
25+
26+
console.log(diff(obj1, obj2));
27+
// [{type: "CREATE", path: ["newProperty"], value: "new"}]
28+
```
29+
30+
There are three different types of changes. `CREATE`, `REMOVE`, and `CHANGE`. The `path` property gives a path to the property in the new object (or the old object in the case of `REMOVE`). Each element in the array is a key to the next property a level deeper until you get to the property changed. The `value` property exists in types `CREATE` and `CHANGE`, and it contains the value of the property added/changed.

bench.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import deepDiff from "deep-diff";
2+
import deepObjectDiff from "deep-object-diff";
3+
import microdiff from "./dist/index.js";
4+
const obj = {
5+
test: "test",
6+
testing: true,
7+
bananas: "awesome",
8+
bestFruits: ["bananas", "kiwi", "blueberries"],
9+
};
10+
const newObj = {
11+
test: "new test",
12+
testing: true,
13+
bananas: "awesome",
14+
bestFruits: ["bananas", "kiwi", "blueberries", "blackberries"],
15+
};
16+
console.time("deep-diff");
17+
deepDiff.diff(obj, newObj);
18+
console.timeEnd("deep-diff");
19+
console.time("deep-object-diff");
20+
deepObjectDiff.detailedDiff(obj, newObj);
21+
console.timeEnd("deep-object-diff");
22+
console.time("microdiff");
23+
microdiff(obj, newObj);
24+
console.timeEnd("microdiff");

index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
interface Difference {
2+
type: "CREATE" | "REMOVE" | "CHANGE";
3+
path: string[];
4+
value?: any;
5+
}
6+
7+
export default function diff(
8+
obj: Record<string, any> | any[],
9+
newObj: Record<string, any> | any[]
10+
): Difference[] {
11+
let diffs: Difference[] = [];
12+
for (const key in obj) {
13+
if (!(key in newObj)) {
14+
diffs.push({
15+
type: "REMOVE",
16+
path: [key],
17+
});
18+
} else if (obj[key] && typeof obj[key] === "object") {
19+
const nestedDiffs = diff(obj[key], newObj[key]);
20+
diffs.push(
21+
...nestedDiffs.map((difference) => {
22+
difference.path.unshift(key);
23+
return difference;
24+
})
25+
);
26+
} else if (obj[key] !== newObj[key]) {
27+
diffs.push({
28+
path: [key],
29+
type: "CHANGE",
30+
value: newObj[key],
31+
});
32+
}
33+
}
34+
for (const key in newObj) {
35+
if (!(key in obj)) {
36+
diffs.push({
37+
type: "CREATE",
38+
path: [key],
39+
value: newObj[key],
40+
});
41+
}
42+
}
43+
return diffs;
44+
}

package.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "microdiff",
3+
"version": "1.0.0",
4+
"description": "Small, fast, zero dependency deep object and array comparison",
5+
"main": "./dist/index.js",
6+
"module": "./dist/index.js",
7+
"types": "./dist/index.d.ts",
8+
"scripts": {
9+
"build": "tsc",
10+
"test": "tsc && uvu tests",
11+
"bench": "tsc && node bench.js"
12+
},
13+
"keywords": [
14+
"diff",
15+
"comparison"
16+
],
17+
"author": "AsyncBanana",
18+
"license": "MIT",
19+
"files": [
20+
"dist"
21+
],
22+
"devDependencies": {
23+
"deep-diff": "^1.0.2",
24+
"deep-object-diff": "^1.1.0",
25+
"typescript": "^4.4.4",
26+
"uvu": "^0.5.2"
27+
},
28+
"type": "module"
29+
}

tests/arrays.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { test } from "uvu";
2+
import * as assert from "uvu/assert";
3+
import diff from "../dist/index.js";
4+
5+
test("top level array & array diff", () => {
6+
assert.equal(diff(["test", "testing"], ["test"]), [
7+
{
8+
type: "REMOVE",
9+
path: ["1"],
10+
},
11+
]);
12+
});
13+
14+
test("nested array", () => {
15+
assert.equal(diff(["test", ["test"]], ["test", ["test", "test2"]]), [
16+
{
17+
type: "CREATE",
18+
path: ["1", "1"],
19+
value: "test2",
20+
},
21+
]);
22+
});
23+
24+
test.run();

tests/basic.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { test } from "uvu";
2+
import * as assert from "uvu/assert";
3+
import diff from "../dist/index.js";
4+
5+
test("new raw value", () => {
6+
assert.equal(diff({ test: true }, { test: true, test2: true }), [
7+
{
8+
type: "CREATE",
9+
path: ["test2"],
10+
value: true,
11+
},
12+
]);
13+
});
14+
test("change raw value", () => {
15+
assert.equal(diff({ test: true }, { test: false }), [
16+
{
17+
type: "CHANGE",
18+
path: ["test"],
19+
value: false,
20+
},
21+
]);
22+
});
23+
test("remove raw value", () => {
24+
assert.equal(diff({ test: true, test2: true }, { test: true }), [
25+
{
26+
type: "REMOVE",
27+
path: ["test2"],
28+
},
29+
]);
30+
});
31+
32+
test.run();

tsconfig.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"files": ["index.ts"],
3+
"buildOptions": {},
4+
"compilerOptions": {
5+
"outDir": "dist",
6+
"declaration": true,
7+
"target": "ES2020",
8+
"moduleResolution": "node",
9+
"module": "ES2020"
10+
}
11+
}

0 commit comments

Comments
 (0)