Skip to content

Commit

Permalink
Add deeply diff
Browse files Browse the repository at this point in the history
  • Loading branch information
Swizz committed Nov 27, 2017
1 parent ce7cf51 commit f5356e2
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 18 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

<br/>

T(a)dis is a 290 Bytes `diff` function returning a patch object, allowing you to
T(a)dis is a 350 Bytes `diff` function returning a patch object, allowing you to
perform time traveling to an object or an array using your own merge functions with ease.

```js
Expand Down Expand Up @@ -55,8 +55,8 @@ const prevCar = {
- [Understand the patch](#understand-the-patch)
- [API](#api)
- [interface patch](#interface-patch--do-object-undo-object-)
- [function diff](#function-diff-from-object--array-to-object--array--patch)
- [function patch](#function-patch-from-object--array-diff-patch-array-boolean-clean-boolean--object--array)
- [function diff](#function-diff-from-object--array-to-object--array-deep-boolean--patch)
- [function patch](#function-patch-from-object--array-diff-patch-deep-boolean-array-boolean-clean-boolean--object--array)
- [Recipes](#recipes)
- [Operation type](#operation-type)
- [Array time traveling](#array-time-traveling)
Expand Down Expand Up @@ -187,16 +187,17 @@ This is the patch object, it will hold two properties :
* do : The patch to use to apply the change to a previous state
* undo : The patch to use to undo the change from the current state
#### function diff: (from: object | array, to: object | array) => patch
#### function diff: (from: object | array, to: object | array, deep: boolean) => patch
The diff function will return the patch object by comparing the second object to
the first one. The patch will help to undo the changes or to redo them later.
the first one. The patch will help to undo the changes or to redo them later.
The deep paramater allow you to create a deep patch object of nested objects.
#### function patch: (from: object | array, diff: patch, array: boolean, clean: boolean) => object | array
#### function patch: (from: object | array, diff: patch, deep: boolean, array: boolean, clean: boolean) => object | array
The patch function is an helpful function to merge the from object with the given
diff patch. Object will be merge into a new object.
If the array paramter is set to true, arrays will be automatically returned.
If the array parameter is set to true, arrays will be automatically returned.
The patch function return an object with all the keys, the deleted properties will
appear as undefined, unless you set the clean parameter to true.
Expand Down
23 changes: 18 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function diff(from, to) {
export function diff(from, to, deep) {
var done = {}
var patch = {
do: {},
Expand All @@ -8,8 +8,17 @@ export function diff(from, to) {
for (var key in to) {
if (key in done || from[key] === to[key]) continue

patch.do[key] = to[key]
patch.undo[key] = from[key]
if (deep && (typeof from[key] === "object" || typeof to[key] === "object")) {
var deep_patch = diff(from[key], to[key], deep)

if (Object.keys(deep_patch.do).length) {
patch.do[key] = deep_patch.do
patch.undo[key] = deep_patch.undo
}
} else {
patch.do[key] = to[key]
patch.undo[key] = from[key]
}

done[key] = true
}
Expand All @@ -31,7 +40,7 @@ export function diff(from, to) {
return patch
}

export function patch(from, diff, array, clean) {
export function patch(from, diff, deep, array, clean) {
var to = {}
for (var key in from) {
if (!(clean && from[key] === undefined)) {
Expand All @@ -40,7 +49,11 @@ export function patch(from, diff, array, clean) {
}
for (var key in diff) {
if (!(clean && diff[key] === undefined)) {
to[key] = diff[key]
if (deep && typeof diff[key] === "object") {
to[key] = patch(to[key], diff[key], deep, array, clean)
} else {
to[key] = diff[key]
}
} else {
delete to[key]
}
Expand Down
102 changes: 102 additions & 0 deletions tests/diff.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,108 @@ describe("nested", () => {
})
})

describe("nested true", () => {
test("diff added", () => {
const from = { a: 1, b: { a: 1, b: 2 } }

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

const patch = diff(from, to, true)

expect(patch.do).toEqual({
b: { c: 3 }
})

expect(patch.undo).toEqual({
b: { c: undefined }
})

const future = { ...from, ...patch.do }
future.b = { ...from.b, ...patch.do.b }

expect(future).toEqual(to)

const past = { ...from, ...patch.undo }
past.b = { ...from.b, ...patch.undo.b }

expect(past).toEqual(from)
})

test("diff deleted", () => {
const from = { a: 1, b: { a: 1, b: 2 } }

const to = { a: 1, b: { a: 1 } }

const patch = diff(from, to, true)

expect(patch.do).toEqual({
b: { b: undefined }
})

expect(patch.undo).toEqual({
b: { b: 2 }
})

const future = { ...from, ...patch.do }
future.b = { ...from.b, ...patch.do.b }

expect(future).toEqual(to)

const past = { ...from, ...patch.undo }
past.b = { ...from.b, ...patch.undo.b }

expect(past).toEqual(from)
})

test("diff updated", () => {
const from = { a: 1, b: { a: 1, b: 2 } }

const to = { a: 1, b: { a: 1, b: 1 } }

const patch = diff(from, to, true)

expect(patch.do).toEqual({
b: { b: 1 }
})

expect(patch.undo).toEqual({
b: { b: 2 }
})

const future = { ...from, ...patch.do }
future.b = { ...from.b, ...patch.do.b }

expect(future).toEqual(to)

const past = { ...from, ...patch.undo }
past.b = { ...from.b, ...patch.undo.b }

expect(past).toEqual(from)
})

test("diff kept", () => {
const from = { a: 1, b: { a: 1, b: 2 } }

const to = { a: 1, b: { a: 1, b: 2 } }

const patch = diff(from, to, true)

expect(patch.do).toEqual({})

expect(patch.undo).toEqual({})

const future = { ...from, ...patch.do }
future.b = { ...from.b, ...patch.do.b }

expect(future).toEqual(to)

const past = { ...from, ...patch.undo }
past.b = { ...from.b, ...patch.undo.b }

expect(past).toEqual(from)
})
})

describe("array", () => {
test("diff added", () => {
const from = [1, 2]
Expand Down
39 changes: 33 additions & 6 deletions tests/patch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ describe("array option", () => {

const present = diff(from, to)

expect(patch(from, present.do, true)).toEqual(to)
expect(patch(from, present.do, undefined, true)).toEqual(to)

expect(patch(to, present.undo, true)).toEqual(from)
expect(patch(to, present.undo, undefined, true)).toEqual(from)
})

test("do not transform object", () => {
Expand All @@ -171,9 +171,9 @@ describe("array option", () => {

const present = diff(from, to)

expect(patch(from, present.do, true)).toEqual(to)
expect(patch(from, present.do, undefined, true)).toEqual(to)

expect(patch(to, present.undo, true)).toEqual(from)
expect(patch(to, present.undo, undefined, true)).toEqual(from)
})
})

Expand All @@ -187,10 +187,10 @@ describe("clean option", () => {
const present = diff(from, to)

const past = patch(to, present.undo)
const past_clean = patch(to, present.undo, undefined, true)
const past_clean = patch(to, present.undo, undefined, undefined, true)

const future = patch(from, present.do)
const future_clean = patch(from, present.do, undefined, true)
const future_clean = patch(from, present.do, undefined, undefined, true)

expect(future).toEqual(to)
expect(future_clean).toEqual(to)
Expand All @@ -212,6 +212,33 @@ describe("clean option", () => {

const present = diff(from, to)

expect(patch(from, present.do, undefined, true, true)).toEqual(to)

expect(patch(to, present.undo, undefined, true, true)).toEqual(from)
})
})


describe("deep option", () => {
test('deep object', () => {
const from = { a: 1, b: { a: 1, b: 2 } }

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

const present = diff(from, to, true)

expect(patch(from, present.do, true)).toEqual(to)

expect(patch(to, present.undo, true)).toEqual(from)
})

test('deep array', () => {
const from = { a: 1, b: [1, 2] }

const to = { a: 1, b: [1, 2, 3] }

const present = diff(from, to, true)

expect(patch(from, present.do, true, true)).toEqual(to)

expect(patch(to, present.undo, true, true)).toEqual(from)
Expand Down

0 comments on commit f5356e2

Please sign in to comment.