Superdiff provides a complete and readable diff for both arrays and objects. Plus, it supports stream and file inputs for handling large datasets efficiently, is battle-tested, has zero dependencies, and is super fast.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
DoneDeal0 1606a01687 feat: add tests 2 years ago
dist feat: add tests 2 years ago
src feat: add tests 2 years ago
test feat: add tests 2 years ago
.gitignore feat: base 2 years ago
README.md feat: add granular output 2 years ago
index.ts feat: base 2 years ago
jest.config.js feat: start list test 2 years ago
package.json feat: add tests 2 years ago
tsconfig.json feat: add build script 2 years ago
tsup.config.ts feat: minify build + add tests 2 years ago
yarn.lock feat: add tests 2 years ago

README.md

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: [
            {
              property: "name",
              previousValue: "joe",
              currentValue: "joe",
              status: "equal",
            },
+           {
+             property: "member",
+             previousValue: true,
+             currentValue: false,
+             status: "updated",
+           },
+           {
+             property: "hobbies",
+             previousValue: ["golf", "football"],
+             currentValue: ["golf", "chess"],
+             status: "updated",
+           },
            {
              property: "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?: {
      property: string;
      previousValue: any;
      currentValue: any;
      status: "added" | "deleted" | "equal" | "moved" | "updated";
      // subDiff is a recursive diff in case of nested subproperties
      subDiff?: SubProperties[];
    }[];
  }[];
};

Options

{
  ignoreArrayOrder?: boolean // false by default,
  showOnly?: {
    statuses: ("added" | "deleted" | "updated" | "equal")[], // [] by default
    granularity?: "basic" | "deep" // basic by default
  }
}
  • ignoreArrayOrder: if 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.

  • showOnly: gives you the option to only return the values whose status interest you. It has two parameters:

    • statuses: status you want to see in the output (ex: ["added", "equal"])
      • granularity:
        • basic only returns the main properties whose status match your request, without taking into account their eventual subproperties.
        • deep return main properties whose status match your request but also their relevant 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";
  }[];
};

Options

{
  showOnly?: ("added" | "deleted" | "moved" | "updated" | "equal")[], // [] by default
}
  • showOnly gives you the option to only return the values whose status interest you (ex: ["added", "equal"]).

isEqual()

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

Checks if two values are equal.

Options

{
  ignoreArrayOrder?: boolean // false by default,
}
  • ignoreArrayOrder: if 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.

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


CREDITS

DoneDeal0

SUPPORT

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



CONTRIBUTING

Pull requests are welcome!