From 923d88c08f42a441afb98d2729a93f4acbf86040 Mon Sep 17 00:00:00 2001 From: omgaz Date: Sun, 18 Oct 2020 11:32:13 +1100 Subject: [PATCH 1/4] [minor] allow option to ignore array ordering --- src/index.js | 20 ++++++- tests/index.js | 141 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 128 insertions(+), 33 deletions(-) diff --git a/src/index.js b/src/index.js index c938d9c..6329e62 100644 --- a/src/index.js +++ b/src/index.js @@ -3,14 +3,24 @@ * Author: Gary Chisholm @omgaz */ +function isArray(toCheck) { + return typeof toCheck === 'object' && Boolean(toCheck.length); +} + +const defaultOptions = { + respectArrayOrder: true, +}; + /** * Read in two objects. Iterate over them and return the differences. * * @param {object} obj1 First object to compare from. * @param {object} obj2 Second object to compare against obj1. + * @param {object} options Configure custom behaviour of diffler. + * @param {boolean} options.respectArrayOrder Whether to care if arrays are ordered defaults to true. * @returns {object} Nested json object of changed properties containing a from and to key. */ -function diffler(obj1, obj2) { +function diffler(obj1, obj2, options = defaultOptions) { var diff = {}; // Iterate over obj1 looking for removals and differences in existing values @@ -29,7 +39,13 @@ function diffler(obj1, obj2) { // If property is an object then we need to recursively go down the rabbit hole else if (typeof obj1Val === 'object') { - var tempDiff = diffler(obj1Val, obj2Val); + let obj1ValForDiff = obj1Val; + let obj2ValForDiff = obj2Val; + if (!options.respectArrayOrder && isArray(obj1Val) && isArray(obj2Val)) { + obj1ValForDiff = obj1Val.sort(); + obj2ValForDiff = obj2Val.sort(); + } + var tempDiff = diffler(obj1ValForDiff, obj2ValForDiff); if (Object.keys(tempDiff).length > 0) { if (tempDiff) { diff[key] = tempDiff; diff --git a/tests/index.js b/tests/index.js index ff69419..6981991 100644 --- a/tests/index.js +++ b/tests/index.js @@ -7,8 +7,8 @@ describe('getDiff', () => { const testObjectA = { name: 'gary' }; const testObjectB = { name: 'gary' }; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 0); - assert.deepEqual(difference, {}); + assert.strictEqual(Object.keys(difference).length, 0); + assert.deepStrictEqual(difference, {}); }); it('should detect a single property change', () => { @@ -16,11 +16,11 @@ describe('getDiff', () => { const testObjectB = { name: 'cindy' }; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 1); - assert.equal(Object.keys(difference.name).length, 2); + assert.strictEqual(Object.keys(difference).length, 1); + assert.strictEqual(Object.keys(difference.name).length, 2); - assert.equal(difference.name.from, 'gary'); - assert.equal(difference.name.to, 'cindy'); + assert.strictEqual(difference.name.from, 'gary'); + assert.strictEqual(difference.name.to, 'cindy'); }); it('should detect no changes', () => { @@ -28,7 +28,7 @@ describe('getDiff', () => { const testObjectB = { name: 'gary' }; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 0); + assert.strictEqual(Object.keys(difference).length, 0); }); it('should detect type changes', () => { @@ -36,11 +36,90 @@ describe('getDiff', () => { const testObjectB = { name: 1 }; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 1); - assert.equal(Object.keys(difference.name).length, 2); + assert.strictEqual(Object.keys(difference).length, 1); + assert.strictEqual(Object.keys(difference.name).length, 2); - assert.equal(difference.name.from, '1'); - assert.equal(difference.name.to, 1); + assert.strictEqual(difference.name.from, '1'); + assert.strictEqual(difference.name.to, 1); + }); + }); + + describe('array checks', () => { + it('returns false when no changes detected', () => { + const testObjectA = { arr: ['one', 'two'] }; + const testObjectB = { arr: ['one', 'two'] }; + const difference = diffler(testObjectA, testObjectB); + assert.strictEqual(Object.keys(difference).length, 0); + assert.deepStrictEqual(difference, {}); + }); + + it('returns change when array order shifted', () => { + const testObjectA = { arr: ['one', 'two', 'three'] }; + const testObjectB = { arr: ['three', 'two', 'one'] }; + const difference = diffler(testObjectA, testObjectB); + assert.strictEqual(Object.keys(difference).length, 1); + assert.deepStrictEqual(difference, { arr: { 0: { from: 'one', to: 'three' }, 2: { from: 'three', to: 'one' } } }); + }); + + it('returns false when array order shifted but respectArrayOrder is false', () => { + const testObjectA = { arr: ['one', 'two', 'three'] }; + const testObjectB = { arr: ['three', 'two', 'one'] }; + const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); + assert.strictEqual(Object.keys(difference).length, 0); + assert.deepStrictEqual(difference, {}); + }); + + it('returns false when array order shifted but respectArrayOrder is false as numbers', () => { + const testObjectA = { arr: [1, 2, 3] }; + const testObjectB = { arr: [2, 3, 1] }; + const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); + assert.strictEqual(Object.keys(difference).length, 0); + assert.deepStrictEqual(difference, {}); + }); + + it('returns false when array order shifted but respectArrayOrder is false as mixed', () => { + const testObjectA = { arr: [1, 'two', 3] }; + const testObjectB = { arr: ['two', 3, 1] }; + const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); + assert.strictEqual(Object.keys(difference).length, 0); + assert.deepStrictEqual(difference, {}); + }); + + it('returns diff when array order shifted for non-primitives and respectArrayOrder is false', () => { + const testObjectA = { + myArray: [{ foo: 'bar' }, { baz: 'bat' }], + }; + + const testObjectB = { + myArray: [{ baz: 'bat' }, { foo: 'bar' }], + }; + const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); + assert.strictEqual(Object.keys(difference).length, 1); + assert.deepStrictEqual(difference, {}); + }); + + it('returns change when array item added', () => { + const testObjectA = { arr: ['one', 'two'] }; + const testObjectB = { arr: ['one', 'two', 'three'] }; + const difference = diffler(testObjectA, testObjectB); + assert.strictEqual(Object.keys(difference).length, 1); + assert.deepStrictEqual(difference, { arr: { 2: { from: null, to: 'three' } } }); + }); + + it('returns change when array item removed', () => { + const testObjectA = { arr: ['one', 'two', 'three'] }; + const testObjectB = { arr: ['one', 'two'] }; + const difference = diffler(testObjectA, testObjectB); + assert.strictEqual(Object.keys(difference).length, 1); + assert.deepStrictEqual(difference, { arr: { 2: { from: 'three', to: null } } }); + }); + + it('returns change and removal when array item removed from middle', () => { + const testObjectA = { arr: ['one', 'two', 'three'] }; + const testObjectB = { arr: ['one', 'three'] }; + const difference = diffler(testObjectA, testObjectB); + assert.strictEqual(Object.keys(difference).length, 1); + assert.deepStrictEqual(difference, { arr: { 1: { from: 'two', to: 'three' }, 2: { from: 'three', to: null } } }); }); }); @@ -58,11 +137,11 @@ describe('getDiff', () => { }; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 1); - assert.equal(Object.keys(difference.weight.value).length, 2); + assert.strictEqual(Object.keys(difference).length, 1); + assert.strictEqual(Object.keys(difference.weight.value).length, 2); - assert.equal(difference.weight.value.from, 80); - assert.equal(difference.weight.value.to, 79); + assert.strictEqual(difference.weight.value.from, 80); + assert.strictEqual(difference.weight.value.to, 79); }); it('should detect multiple nested property change', () => { @@ -78,17 +157,17 @@ describe('getDiff', () => { }; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 2); - assert.equal(Object.keys(difference.weight).length, 2); + assert.strictEqual(Object.keys(difference).length, 2); + assert.strictEqual(Object.keys(difference.weight).length, 2); - assert.equal(difference.age.from, 33); - assert.equal(difference.age.to, 34); + assert.strictEqual(difference.age.from, 33); + assert.strictEqual(difference.age.to, 34); - assert.equal(difference.weight.value.from, 80); - assert.equal(difference.weight.value.to, 12.4); + assert.strictEqual(difference.weight.value.from, 80); + assert.strictEqual(difference.weight.value.to, 12.4); - assert.equal(difference.weight.unit.from, 'kg'); - assert.equal(difference.weight.unit.to, 'stone'); + assert.strictEqual(difference.weight.unit.from, 'kg'); + assert.strictEqual(difference.weight.unit.to, 'stone'); }); }); @@ -98,11 +177,11 @@ describe('getDiff', () => { const testObjectB = {}; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 1); - assert.equal(Object.keys(difference.name).length, 2); + assert.strictEqual(Object.keys(difference).length, 1); + assert.strictEqual(Object.keys(difference.name).length, 2); - assert.equal(difference.name.from, 'gary'); - assert.equal(difference.name.to, null); + assert.strictEqual(difference.name.from, 'gary'); + assert.strictEqual(difference.name.to, null); }); it('should detect a nested property removal as null', () => { @@ -114,11 +193,11 @@ describe('getDiff', () => { const testObjectB = { name: 'gary', age: 33 }; const difference = diffler(testObjectA, testObjectB); - assert.equal(Object.keys(difference).length, 1); - assert.equal(Object.keys(difference.weight).length, 2); + assert.strictEqual(Object.keys(difference).length, 1); + assert.strictEqual(Object.keys(difference.weight).length, 2); - assert.deepEqual(difference.weight.from, { unit: 'kg', value: 80 }); - assert.equal(difference.weight.to, null); + assert.deepStrictEqual(difference.weight.from, { unit: 'kg', value: 80 }); + assert.strictEqual(difference.weight.to, null); }); }); }); From 84151051e79cc1ea0fd357d8ab1674990c13cf0a Mon Sep 17 00:00:00 2001 From: omgaz Date: Sun, 18 Oct 2020 12:27:28 +1100 Subject: [PATCH 2/4] [minor] ignore sort ordering with objects --- src/index.js | 18 +++++++++++++----- tests/index.js | 46 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index 6329e62..77055c0 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,15 @@ function isArray(toCheck) { return typeof toCheck === 'object' && Boolean(toCheck.length); } -const defaultOptions = { +function isPrimitive(toCheck) { + return toCheck !== Object(toCheck); +} + +function toPrimitive(val) { + return isPrimitive(val) ? val : JSON.stringify(val); +} + +var defaultOptions = { respectArrayOrder: true, }; @@ -39,11 +47,11 @@ function diffler(obj1, obj2, options = defaultOptions) { // If property is an object then we need to recursively go down the rabbit hole else if (typeof obj1Val === 'object') { - let obj1ValForDiff = obj1Val; - let obj2ValForDiff = obj2Val; + var obj1ValForDiff = obj1Val; + var obj2ValForDiff = obj2Val; if (!options.respectArrayOrder && isArray(obj1Val) && isArray(obj2Val)) { - obj1ValForDiff = obj1Val.sort(); - obj2ValForDiff = obj2Val.sort(); + obj1ValForDiff = obj1Val.map(toPrimitive).sort(); + obj2ValForDiff = obj2Val.map(toPrimitive).sort(); } var tempDiff = diffler(obj1ValForDiff, obj2ValForDiff); if (Object.keys(tempDiff).length > 0) { diff --git a/tests/index.js b/tests/index.js index 6981991..28f8a88 100644 --- a/tests/index.js +++ b/tests/index.js @@ -58,13 +58,20 @@ describe('getDiff', () => { const testObjectB = { arr: ['three', 'two', 'one'] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); - assert.deepStrictEqual(difference, { arr: { 0: { from: 'one', to: 'three' }, 2: { from: 'three', to: 'one' } } }); + assert.deepStrictEqual(difference, { + arr: { + 0: { from: 'one', to: 'three' }, + 2: { from: 'three', to: 'one' }, + }, + }); }); it('returns false when array order shifted but respectArrayOrder is false', () => { const testObjectA = { arr: ['one', 'two', 'three'] }; const testObjectB = { arr: ['three', 'two', 'one'] }; - const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); + const difference = diffler(testObjectA, testObjectB, { + respectArrayOrder: false, + }); assert.strictEqual(Object.keys(difference).length, 0); assert.deepStrictEqual(difference, {}); }); @@ -72,7 +79,9 @@ describe('getDiff', () => { it('returns false when array order shifted but respectArrayOrder is false as numbers', () => { const testObjectA = { arr: [1, 2, 3] }; const testObjectB = { arr: [2, 3, 1] }; - const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); + const difference = diffler(testObjectA, testObjectB, { + respectArrayOrder: false, + }); assert.strictEqual(Object.keys(difference).length, 0); assert.deepStrictEqual(difference, {}); }); @@ -80,21 +89,25 @@ describe('getDiff', () => { it('returns false when array order shifted but respectArrayOrder is false as mixed', () => { const testObjectA = { arr: [1, 'two', 3] }; const testObjectB = { arr: ['two', 3, 1] }; - const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); + const difference = diffler(testObjectA, testObjectB, { + respectArrayOrder: false, + }); assert.strictEqual(Object.keys(difference).length, 0); assert.deepStrictEqual(difference, {}); }); - it('returns diff when array order shifted for non-primitives and respectArrayOrder is false', () => { + it('returns no diff when array order shifted for non-primitives and respectArrayOrder is false', () => { const testObjectA = { - myArray: [{ foo: 'bar' }, { baz: 'bat' }], + myArray: ['a string', { foo: 'bar' }, 1, { baz: 'bat' }], }; const testObjectB = { - myArray: [{ baz: 'bat' }, { foo: 'bar' }], + myArray: [{ baz: 'bat' }, { foo: 'bar' }, 1, 'a string'], }; - const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false }); - assert.strictEqual(Object.keys(difference).length, 1); + const difference = diffler(testObjectA, testObjectB, { + respectArrayOrder: false, + }); + assert.strictEqual(Object.keys(difference).length, 0); assert.deepStrictEqual(difference, {}); }); @@ -103,7 +116,9 @@ describe('getDiff', () => { const testObjectB = { arr: ['one', 'two', 'three'] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); - assert.deepStrictEqual(difference, { arr: { 2: { from: null, to: 'three' } } }); + assert.deepStrictEqual(difference, { + arr: { 2: { from: null, to: 'three' } }, + }); }); it('returns change when array item removed', () => { @@ -111,7 +126,9 @@ describe('getDiff', () => { const testObjectB = { arr: ['one', 'two'] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); - assert.deepStrictEqual(difference, { arr: { 2: { from: 'three', to: null } } }); + assert.deepStrictEqual(difference, { + arr: { 2: { from: 'three', to: null } }, + }); }); it('returns change and removal when array item removed from middle', () => { @@ -119,7 +136,12 @@ describe('getDiff', () => { const testObjectB = { arr: ['one', 'three'] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); - assert.deepStrictEqual(difference, { arr: { 1: { from: 'two', to: 'three' }, 2: { from: 'three', to: null } } }); + assert.deepStrictEqual(difference, { + arr: { + 1: { from: 'two', to: 'three' }, + 2: { from: 'three', to: null }, + }, + }); }); }); From dd507398f7302705b52e6a529391b236f2cf8946 Mon Sep 17 00:00:00 2001 From: omgaz Date: Sun, 18 Oct 2020 13:19:33 +1100 Subject: [PATCH 3/4] [major] respectArrayOrder --- README.md | 48 +++++++++++++ src/index.js | 27 +++++--- tests/index.js | 182 +++++++++++++++++++++++++++++-------------------- 3 files changed, 172 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index d228c66..f4c848b 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,54 @@ const difference = diffler(before, after); console.log(difference); // { location: { from: "London", to: "Melbourne" } } ``` +## Options + +> Experimental + +```shell +npm i diffler@next +``` + +### `respectArrayOrder` defaults to `true` + +If you don't care about the order of an array you can disable like so: + +```js +const diffler = require("diffler"); + +const before = { name: "omgaz", locations: ["London", "Melbourne"] }; +const after = { name: "omgaz", locations: ["Melbourne", "London"] }; + +const difference = diffler(before, after, { respectArrayOrder: false }); +console.log(difference); // {} +``` + +However, be aware that additions and removals change the overall shape of the array and so will be seen as an entire array change AFTER the addition and removal. + +```js +const diffler = require("diffler"); + +const before = { name: "omgaz", locations: ["London", "Hong Kong", "Melbourne"] }; +const after = { name: "omgaz", locations: ["London", "Melbourne", "Hong Kong" ] }; + +const difference = diffler(before, after, { respectArrayOrder: false }); +console.log(difference); // { locations: { 1: { from: "Hong Kong", to: "Melbourne" }, 2: { from: "Melbourne", to: "Hong Kong" } +``` + +My advice is to use associative arrays if you do not care about order. + +```js +const diffler = require("diffler"); + +const before = { name: "omgaz", locations: { Melbourne: "Melbourne", London: "London" } }; +const after = { name: "omgaz", locations: { London: "London", Melbourne: "Melbourne" } }; + +const difference = diffler(before, after, { respectArrayOrder: false }); +console.log(difference); // { } +``` + +Maybe in the future, diffler will do this internally and be able to return array new array indexes for these values, it is still my current belief that arrays are ordered and should preserve order. + ## Tests [![Build Status](https://travis-ci.org/omgaz/diffler.svg?branch=master)](https://travis-ci.org/omgaz/diffler) diff --git a/src/index.js b/src/index.js index 77055c0..0ca4ca8 100644 --- a/src/index.js +++ b/src/index.js @@ -4,15 +4,16 @@ */ function isArray(toCheck) { - return typeof toCheck === 'object' && Boolean(toCheck.length); + return typeof toCheck === "object" && Boolean(toCheck.length); } function isPrimitive(toCheck) { return toCheck !== Object(toCheck); } -function toPrimitive(val) { - return isPrimitive(val) ? val : JSON.stringify(val); +function validate(val) { + if (isPrimitive(val)) return val; + throw new Error("Cannot ignore array ordering on non-primitive values"); } var defaultOptions = { @@ -33,7 +34,7 @@ function diffler(obj1, obj2, options = defaultOptions) { // Iterate over obj1 looking for removals and differences in existing values for (var key in obj1) { - if (obj1.hasOwnProperty(key) && typeof obj1[key] !== 'function') { + if (obj1.hasOwnProperty(key) && typeof obj1[key] !== "function") { var obj1Val = obj1[key], obj2Val = obj2[key]; @@ -46,14 +47,20 @@ function diffler(obj1, obj2, options = defaultOptions) { } // If property is an object then we need to recursively go down the rabbit hole - else if (typeof obj1Val === 'object') { + else if (typeof obj1Val === "object") { var obj1ValForDiff = obj1Val; var obj2ValForDiff = obj2Val; - if (!options.respectArrayOrder && isArray(obj1Val) && isArray(obj2Val)) { - obj1ValForDiff = obj1Val.map(toPrimitive).sort(); - obj2ValForDiff = obj2Val.map(toPrimitive).sort(); + if (isArray(obj1Val) && isArray(obj2Val)) { + // TODO: This is messy, but I've found a bug with arrays of mixed types + // I need to clean this up later. + obj1Val.map(validate); + obj2Val.map(validate); + if (!options.respectArrayOrder) { + obj1ValForDiff = obj1Val.sort(); + obj2ValForDiff = obj2Val.sort(); + } } - var tempDiff = diffler(obj1ValForDiff, obj2ValForDiff); + var tempDiff = diffler(obj1ValForDiff, obj2ValForDiff, options); if (Object.keys(tempDiff).length > 0) { if (tempDiff) { diff[key] = tempDiff; @@ -73,7 +80,7 @@ function diffler(obj1, obj2, options = defaultOptions) { // Iterate over obj2 looking for any new additions for (key in obj2) { - if (obj2.hasOwnProperty(key) && typeof obj2[key] !== 'function') { + if (obj2.hasOwnProperty(key) && typeof obj2[key] !== "function") { var obj1Val = obj1[key], obj2Val = obj2[key]; diff --git a/tests/index.js b/tests/index.js index 28f8a88..265d8cf 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,74 +1,74 @@ -const assert = require('assert'); -const diffler = require('../src'); - -describe('getDiff', () => { - describe('simple checks', () => { - it('returns false when no changes detected', () => { - const testObjectA = { name: 'gary' }; - const testObjectB = { name: 'gary' }; +const assert = require("assert"); +const diffler = require("../src"); + +describe("getDiff", () => { + describe("simple checks", () => { + it("returns false when no changes detected", () => { + const testObjectA = { name: "gary" }; + const testObjectB = { name: "gary" }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 0); assert.deepStrictEqual(difference, {}); }); - it('should detect a single property change', () => { - const testObjectA = { name: 'gary' }; - const testObjectB = { name: 'cindy' }; + it("should detect a single property change", () => { + const testObjectA = { name: "gary" }; + const testObjectB = { name: "cindy" }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.strictEqual(Object.keys(difference.name).length, 2); - assert.strictEqual(difference.name.from, 'gary'); - assert.strictEqual(difference.name.to, 'cindy'); + assert.strictEqual(difference.name.from, "gary"); + assert.strictEqual(difference.name.to, "cindy"); }); - it('should detect no changes', () => { - const testObjectA = { name: 'gary' }; - const testObjectB = { name: 'gary' }; + it("should detect no changes", () => { + const testObjectA = { name: "gary" }; + const testObjectB = { name: "gary" }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 0); }); - it('should detect type changes', () => { - const testObjectA = { name: '1' }; + it("should detect type changes", () => { + const testObjectA = { name: "1" }; const testObjectB = { name: 1 }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.strictEqual(Object.keys(difference.name).length, 2); - assert.strictEqual(difference.name.from, '1'); + assert.strictEqual(difference.name.from, "1"); assert.strictEqual(difference.name.to, 1); }); }); - describe('array checks', () => { - it('returns false when no changes detected', () => { - const testObjectA = { arr: ['one', 'two'] }; - const testObjectB = { arr: ['one', 'two'] }; + describe("array checks", () => { + it("returns false when no changes detected", () => { + const testObjectA = { arr: ["one", "two"] }; + const testObjectB = { arr: ["one", "two"] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 0); assert.deepStrictEqual(difference, {}); }); - it('returns change when array order shifted', () => { - const testObjectA = { arr: ['one', 'two', 'three'] }; - const testObjectB = { arr: ['three', 'two', 'one'] }; + it("returns change when array order shifted", () => { + const testObjectA = { arr: ["one", "two", "three"] }; + const testObjectB = { arr: ["three", "two", "one"] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.deepStrictEqual(difference, { arr: { - 0: { from: 'one', to: 'three' }, - 2: { from: 'three', to: 'one' }, + 0: { from: "one", to: "three" }, + 2: { from: "three", to: "one" }, }, }); }); - it('returns false when array order shifted but respectArrayOrder is false', () => { - const testObjectA = { arr: ['one', 'two', 'three'] }; - const testObjectB = { arr: ['three', 'two', 'one'] }; + it("returns false when array order shifted but respectArrayOrder is false", () => { + const testObjectA = { arr: ["one", "two", "three"] }; + const testObjectB = { arr: ["three", "two", "one"] }; const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false, }); @@ -76,7 +76,7 @@ describe('getDiff', () => { assert.deepStrictEqual(difference, {}); }); - it('returns false when array order shifted but respectArrayOrder is false as numbers', () => { + it("returns false when array order shifted but respectArrayOrder is false as numbers", () => { const testObjectA = { arr: [1, 2, 3] }; const testObjectB = { arr: [2, 3, 1] }; const difference = diffler(testObjectA, testObjectB, { @@ -86,9 +86,9 @@ describe('getDiff', () => { assert.deepStrictEqual(difference, {}); }); - it('returns false when array order shifted but respectArrayOrder is false as mixed', () => { - const testObjectA = { arr: [1, 'two', 3] }; - const testObjectB = { arr: ['two', 3, 1] }; + it("returns false when array order shifted but respectArrayOrder is false as mixed", () => { + const testObjectA = { arr: [1, "two", 3] }; + const testObjectB = { arr: ["two", 3, 1] }; const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false, }); @@ -96,66 +96,98 @@ describe('getDiff', () => { assert.deepStrictEqual(difference, {}); }); - it('returns no diff when array order shifted for non-primitives and respectArrayOrder is false', () => { + it("throws an error if arrays contain primitives and objects", () => { const testObjectA = { - myArray: ['a string', { foo: 'bar' }, 1, { baz: 'bat' }], + myArray: ["a string", { foo: "bar" }, 1, { baz: "bat" }], }; const testObjectB = { - myArray: [{ baz: 'bat' }, { foo: 'bar' }, 1, 'a string'], + myArray: [{ baz: "bat" }, { foo: "bar" }, 1, "a string"], + }; + assert.throws(() => diffler(testObjectA, testObjectB)); + }); + + // this behaviour seems odd if we don't care about array ordering, I'm not sure if adding one item + // should then break the entire object? + it("returns a single addition when respectArrayOrder is false and one item added", () => { + const testObjectA = { + myArray: ["a string", "item", 4], + }; + + const testObjectB = { + myArray: ["item", 4, 1, "a string"], }; const difference = diffler(testObjectA, testObjectB, { respectArrayOrder: false, }); - assert.strictEqual(Object.keys(difference).length, 0); - assert.deepStrictEqual(difference, {}); + assert.strictEqual(Object.keys(difference).length, 1); + assert.deepStrictEqual(difference, { + myArray: { + 0: { + from: 4, + to: 1, + }, + 1: { + from: "a string", + to: 4, + }, + 2: { + from: "item", + to: "a string", + }, + 3: { + from: null, + to: "item", + }, + }, + }); }); - it('returns change when array item added', () => { - const testObjectA = { arr: ['one', 'two'] }; - const testObjectB = { arr: ['one', 'two', 'three'] }; + it("returns change when array item added", () => { + const testObjectA = { arr: ["one", "two"] }; + const testObjectB = { arr: ["one", "two", "three"] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.deepStrictEqual(difference, { - arr: { 2: { from: null, to: 'three' } }, + arr: { 2: { from: null, to: "three" } }, }); }); - it('returns change when array item removed', () => { - const testObjectA = { arr: ['one', 'two', 'three'] }; - const testObjectB = { arr: ['one', 'two'] }; + it("returns change when array item removed", () => { + const testObjectA = { arr: ["one", "two", "three"] }; + const testObjectB = { arr: ["one", "two"] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.deepStrictEqual(difference, { - arr: { 2: { from: 'three', to: null } }, + arr: { 2: { from: "three", to: null } }, }); }); - it('returns change and removal when array item removed from middle', () => { - const testObjectA = { arr: ['one', 'two', 'three'] }; - const testObjectB = { arr: ['one', 'three'] }; + it("returns change and removal when array item removed from middle", () => { + const testObjectA = { arr: ["one", "two", "three"] }; + const testObjectB = { arr: ["one", "three"] }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.deepStrictEqual(difference, { arr: { - 1: { from: 'two', to: 'three' }, - 2: { from: 'three', to: null }, + 1: { from: "two", to: "three" }, + 2: { from: "three", to: null }, }, }); }); }); - describe('multiple checks', () => { - it('should detect a nested property change', () => { + describe("multiple checks", () => { + it("should detect a nested property change", () => { const testObjectA = { - name: 'gary', + name: "gary", age: 33, - weight: { unit: 'kg', value: 80 }, + weight: { unit: "kg", value: 80 }, }; const testObjectB = { - name: 'gary', + name: "gary", age: 33, - weight: { unit: 'kg', value: 79 }, + weight: { unit: "kg", value: 79 }, }; const difference = diffler(testObjectA, testObjectB); @@ -166,16 +198,16 @@ describe('getDiff', () => { assert.strictEqual(difference.weight.value.to, 79); }); - it('should detect multiple nested property change', () => { + it("should detect multiple nested property change", () => { const testObjectA = { - name: 'gary', + name: "gary", age: 33, - weight: { unit: 'kg', value: 80 }, + weight: { unit: "kg", value: 80 }, }; const testObjectB = { - name: 'gary', + name: "gary", age: 34, - weight: { unit: 'stone', value: 12.4 }, + weight: { unit: "stone", value: 12.4 }, }; const difference = diffler(testObjectA, testObjectB); @@ -188,37 +220,37 @@ describe('getDiff', () => { assert.strictEqual(difference.weight.value.from, 80); assert.strictEqual(difference.weight.value.to, 12.4); - assert.strictEqual(difference.weight.unit.from, 'kg'); - assert.strictEqual(difference.weight.unit.to, 'stone'); + assert.strictEqual(difference.weight.unit.from, "kg"); + assert.strictEqual(difference.weight.unit.to, "stone"); }); }); - describe('property removals', () => { - it('should detect a single property removal as null', () => { - const testObjectA = { name: 'gary' }; + describe("property removals", () => { + it("should detect a single property removal as null", () => { + const testObjectA = { name: "gary" }; const testObjectB = {}; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.strictEqual(Object.keys(difference.name).length, 2); - assert.strictEqual(difference.name.from, 'gary'); + assert.strictEqual(difference.name.from, "gary"); assert.strictEqual(difference.name.to, null); }); - it('should detect a nested property removal as null', () => { + it("should detect a nested property removal as null", () => { const testObjectA = { - name: 'gary', + name: "gary", age: 33, - weight: { unit: 'kg', value: 80 }, + weight: { unit: "kg", value: 80 }, }; - const testObjectB = { name: 'gary', age: 33 }; + const testObjectB = { name: "gary", age: 33 }; const difference = diffler(testObjectA, testObjectB); assert.strictEqual(Object.keys(difference).length, 1); assert.strictEqual(Object.keys(difference.weight).length, 2); - assert.deepStrictEqual(difference.weight.from, { unit: 'kg', value: 80 }); + assert.deepStrictEqual(difference.weight.from, { unit: "kg", value: 80 }); assert.strictEqual(difference.weight.to, null); }); }); From 5590edf93bc5f4bb97cbef273b022803549524de Mon Sep 17 00:00:00 2001 From: omgaz Date: Sun, 18 Oct 2020 13:31:14 +1100 Subject: [PATCH 4/4] build --- dist/index.js | 2 +- src/index.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index eaceadd..38a496a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2,4 +2,4 @@ * @license MIT https://github.com/omgaz/diffler * Author: Gary Chisholm @omgaz */ -function diffler(o,f){var n,r,t,e,i={};for(n in o){o.hasOwnProperty(n)&&"function"!=typeof o[n]&&(t=o[n],e=f[n],n in f?"object"==typeof t?(r=diffler(t,e),0