The most known way to travel in space and time
JavaScript
Latest commit ccd9ccd Jan 15, 2018 @Swizz Swizz Merge pull request #1 from marudor/master
Typo README.md
Permalink
Failed to load latest commit information.
src Fix length bug Jan 9, 2018
tests Add deeply diff Nov 27, 2017
.gitignore Diff-ing algorithm Nov 7, 2017
LICENSE.md Add license file Nov 11, 2017
README.md Typo Jan 14, 2018
package.json 1.0.0 Jan 9, 2018

README.md

t(a)rdis header

T(a)rdis

The most known way to travel in space and time


npm version mit license travis build


T(a)rdis 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.

import { diff } from 'trdis'

const patch = diff({
  type: "Fiat", model: "Punto"
}, {
  type: "Fiat", model: "500", color: "red"
})

const newCar = {
  type: "Fiat", model: "Punto", ...patch.do
}

const prevCar = {
  type: "Fiat", model: "500", color: "red", ...patch.undo
}

Table of Contents

Table of Contents

Getting started

  • 1. To use T(a)rdis you need to download it thanks to your favorite JavaScript Package Manager.

    yarn add trdis
    npm install --save trdis
  • 2. Now you need to import explicitly the exported diff function.

    import { diff } from 'trdis'
  • 3. Use the diff function to generate the patch object.

    const patch = diff(from, to)

Usage

The diff function is your way to obtain the patch object, the present. By providing both the source object or array, the past, and the target object or array, the future.

const present = diff(past, future)

The patch object will contain two properties the do and the undo inscructions.
Both of them are mergeable objects. That way you are able to use your favorite merge function to apply the patch.
Lets use the objects spread operator as merge function.

The undo object allow you to travel to the previous state of the object or array.

const past = { ...future, ...present.undo }

Yes! You have understood! The do object is for the future.

const future = { ...past, ...present.do }

Here we have seen that the do and the undo patch are compliant with the objects spread operator.
You are right! The both patch functions are plain object ready to overide the present object or array properties to apply the patch.

So as we discovered earlier every merge functions will a be great candidates as our patch function.

Using patch function

T(a)rdis provide you a patch function, ready to counter all the T(a)rdis diff function features. This function is aligned with the diff signature to allow you to take all benefits of T(a)rdis with a sanitized experience.

The patch function provide an option to automatically return well structured arrays ; and another one to remove undefined/deleted properties.

let past = patch(future, present.undo)
let future = patch(past, present.do)

Using Object.assign

Object.assign is a JS function used to copy the values from one (or more) object(s) into another one.
This function could be used in two way the first one will mutate the source object, and the second one, will use an empty object to create a copy.

Object.assign(present, future.undo)
Object.assign(present, future.do)

Using Object.assign that way will mutate the present object acordingly to the future patch.

The following one, will by opposition, create a new object by applying the patch.

let past = Object.assign({}, future, present.undo)
let future = Object.assign({}, past, present.do)

Using your own merge

Now you understood, we are now able to generalise using only one function, your own merge function.

let past = merge(future, present.undo)
let future = merge(past, present.do)

Understand the patch

Lets dig a bit futher into our understanding of the patch object.
The patch hold two properties, the do and the undo.

Both are simple objects, ready to be merged with the current object to move forward or backward your changes.
Looking at do and undo, you will be able to understand what kind of operation it was performed on the object.

To sum up, there is only three kind of operation : insert, update, delete.

API

interface patch: { do: object, undo: object }

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, 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 deep paramater allow you to create a deep patch object of nested objects.

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 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.

Recipes

Operation type

Remember how we understood the patch ? We seen we can easily write a function to return the kind of operation we performed on a given key.

Lets try it.

function kind(patch, key) {
  if(!(key in patch.do) && !(key in patch.undo)) {
    return "kept"
  } else if (patch.do[key] === undefined && patch.undo[key] !== undefined) {
    return "deleted"
  } else if (patch.do[key] !== undefined && patch.undo[key] === undefined) {
    return "added"
  } else {
    return "updated"
  }
}

Here we are testing all the possibilities, by making a simple statement :

If for performing the do patch I need to make undefined the value and to apply the undo patch I need a value, so at start I deleted the value

As you see, we can simplify the statement with :

If for performing the do patch I need to make undefined the value, so at start I deleted the value

So, a more simple kind function could be the following one.

function kind(patch, key) {
  if(!(key in patch.do) && !(key in patch.undo)) {
    return "kept"
  } else if (patch.do[key] === undefined) {
    return "deleted"
  } else if (patch.undo[key] === undefined) {
    return "added"
  } else {
    return "updated"
  }
}

Array time traveling

With T(a)rdis you are ready to time travel with an array too, as in JavaScript arrays are objects. T(a)rdis is aware of all array properties and will perform diff checking that allow you to reconscruct an array with ease.

const past = [1, 2, 3]
const future = [1, 2, 3, 4]

const present = diff(past, future)

const future = Array.from({ ...past, ...present.do })
// present: (4)[1, 2, 3, 4]

Objects equality

Just as said earlier, T(a)rdis help you to obtain a patch representing the differences between two objects.

Now, we are able to make a simple statement :

If the diff of two objects is empty, this is because the two objects are equals!

function equality (a, b) {
  const patch = diff(a, b)
  return Object.keys(patch.do).length === 0 && Object.keys(patch.undo).length === 0
}

Object copy

Using the patch returned the diff function of T(a)rdis, you are also able to perform a lot of amuzing kind of operation on you object.

For example, you are able to create a copy of your source object, apply and undoing the patch!

const from = { a: 1, b: 2 }
const patch = diff(from, {})

const copy = { ...from, ...patch.do, ...patch.undo }
// copy: { a: 1, b:2 }

Pretty easy! Now, add yours!


T(a)rdis is made by Swizz.