Skip to content

DoneDeal0/superdiff

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 

superdiff-logo

SUPERDIFF

This library compares two arrays or objects and return a complete diff of their differences.

WHY YOU SHOULD USE THIS LIB

All other existing solutions return a weird diff format which often require an additional parsing. They are also slow and limited to object comparison. 👎

Superdiff gives you a complete diff for both array and objects with a very readable format. Last but not least, it's battled tested and super fast. Import. Enjoy. 👍

Benchmark:

Objects Deep-diff 🐢 Superdiff
1.000 10.47ms 5.73ms
10.000 43.05ms 18.60ms
100.000 289.71ms 50.96ms
1.000.000 2786.70ms 389.78ms

DIFF FORMAT COMPARISON

Let's compare the diff format of Superdiff and Deep-diff, the most popular diff lib on npm:

input:

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,
        },
  }

Deep-Diff output:

[
 DiffEdit {
   kind: 'E',
   path: [ 'user', 'member' ],
   lhs: true,
   rhs: false
 },
 DiffEdit {
   kind: 'E',
   path: [ 'user', 'hobbies', 1 ],
   lhs: 'football',
   rhs: 'chess'
 }
]

SuperDiff output:

{
      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: [
            {
              name: "name",
              previousValue: "joe",
              currentValue: "joe",
              status: "equal",
            },
+           {
+             name: "member",
+             previousValue: true,
+             currentValue: false,
+             status: "updated",
+           },
+           {
+             name: "hobbies",
+             previousValue: ["golf", "football"],
+             currentValue: ["golf", "chess"],
+             status: "updated",
+           },
            {
              name: "age",
              previousValue: 66,
              currentValue: 66,
              status: "equal",
            },
          ],
        },
      ],
    }

FEATURES

Superdiff exports 4 functions:

getObjectDiff()

import { getObjectDiff } from "@donedeal0/superdiff";

Compares two objects and return a diff for each value and their potential subvalues:

  • property name
  • status: added, deleted, equal, updated
  • previous value, current value
  • supports deeply nested objects with any kind of values

format:

type ObjectDiff = {
  type: "object";
  status: "added" | "deleted" | "equal" | "moved" | "updated";
  diff: {
    property: string;
    previousValue: any;
    currentValue: any;
    status: "added" | "deleted" | "equal" | "moved" | "updated";
    // only appears if some subproperties have been added/deleted/updated
    subPropertiesDiff?: {
      name: string;
      previousValue: any;
      currentValue: any;
      status: "added" | "deleted" | "equal" | "moved" | "updated";
      // subDiff is a recursive diff in case of nested subproperties
      subDiff?: Subproperties[];
    }[];
  }[];
};

getListDiff()

import { getListDiff } from "@donedeal0/superdiff";

Compares two arrays and return a diff for each value:

  • index change: prevIndex, newIndex, indexDiff
  • status: added, deleted, equal, moved, updated
  • value
  • supports arrays of primitive values and objects
  • supports arrays with duplicated values

format:

type ListDiff = {
  type: "list";
  status: "added" | "deleted" | "equal" | "moved" | "updated";
  diff: {
    value: any;
    prevIndex: number | null;
    newIndex: number | null;
    indexDiff: number | null;
    status: "added" | "deleted" | "equal" | "moved" | "updated";
  }[];
};

isEqual()

import { isEqual } from "@donedeal0/superdiff";

Checks if two values are equal.

isObject()

import { isObject } from "@donedeal0/superdiff";

Checks if a value is an object.

EXAMPLES

getListDiff()

input

getListDiff(
- ["mbappe", "mendes", "verratti", "ruiz"],
+ ["mbappe", "messi", "ruiz"]
);

output

{
      type: "list",
+     status: "updated",
      diff: [
        {
          value: "mbappe",
          prevIndex: 0,
          newIndex: 0,
          indexDiff: 0,
          status: "equal",
        },
-       {
-         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",
        },
      ],
    }

getObjectDiff()

input

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

{
      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: [
            {
              name: "name",
              previousValue: "joe",
              currentValue: "joe",
              status: "equal",
            },
+           {
+             name: "member",
+             previousValue: true,
+             currentValue: false,
+             status: "updated",
+           },
+           {
+             name: "hobbies",
+             previousValue: ["golf", "football"],
+             currentValue: ["golf", "chess"],
+             status: "updated",
+           },
            {
              name: "age",
              previousValue: 66,
              currentValue: 66,
              status: "equal",
            },
          ],
        },
      ],
    }

isEqual()

isEqual(
  [
    { name: "joe", age: 99 },
    { name: "nina", age: 23 },
  ],
  [
    { name: "joe", age: 98 },
    { name: "nina", age: 23 },
  ]
);

output

false;

isObject()

input

isObject(["hello", "world"]);

output

false;

More examples are availble in the tests of the source code.


OPTIONS

getObjectDiff() and isEqual() accept a facultative options parameter:

{
  discardArrayOrder?: boolean // false by default
}

If discardArrayOrder is set to true, ["hello", "world"] and ["world", "hello"] will be considered as equal, because the two arrays have the same value, just not in the same order.

CREDITS

DoneDeal0

SUPPORT

If you use Superdiff, please show your support by buying me coffee: https://www.buymeacoffee.com/donedeal0



CONTRIBUTING

Pull requests are welcome!