mattphillips / deep-object-diff

Deep diffs two objects, including nested structures of arrays and objects, and returns the difference. ❄️

Home Page:https://www.npmjs.com/package/deep-object-diff

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Keep arrays as arrays

marciorodrigues87 opened this issue · comments

Hi guys! First of all, great work on this project!

I was thinking if you find it useful to keep arrays as arrays on the resulting diff object, something like:

      let difference = diff(l[key], r[key])
      if (Array.isArray(l[key])) {
        difference = Object.entries(difference).map(d => d[1])
      }

If it's interesting to you I can submit a PR to do that!

Hey @marciorodrigues87 by converting the object representation back to an array with:

Object.entries(difference).map(d => d[1]);

you would lose the original index of the array where the difference occured.

Could you (or @webyonet) show me an example of what you would like the difference to look like for a given left and right object with arrays values?

The issue with preserving an array when performing a diff is what do you do with the indices that do not change? There are 3 types of operations that can cause a diff added, deleted and updated and their placement in an array can have knock on affects to the rest of the data structure. I've written out examples of the types of diffs that can occur in an array (not including nested objects but the logic is the same) and highlighted the results of each diff below.

// added (start of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['d', 'a', 'b', 'c'] };
diff(left, right);
/*
{
  alphabet: { 0: 'd', 1: 'a', 2: 'b', 3: 'c' }
}
*/

// added (end of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['a', 'b', 'c', 'd'] };
diff(left, right);
/*
{
  alphabet: { 3: 'd' }
}
*/

// deleted (start of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['b', 'c'] };
diff(left, right);
/*
{
  alphabet: { 0: 'b', 1: 'c', 2: undefined }
}
*/

// deleted (end of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['a', 'b'] };
diff(left, right);
/*
{
  alphabet: { 2: undefined }
}
*/

// updated
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['z', 'b', 'c'] };
diff(left, right);
/*
{
  alphabet: { 0: 'z' }
}
*/

What would you want the output to be? Something like:

// added (start of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['d', 'a', 'b', 'c'] };
diff(left, right);
/*
{
  alphabet: ['d', 'a', 'b', 'c']
}
*/

// added (end of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['a', 'b', 'c', 'd'] };
diff(left, right);
/*
{
  alphabet: [empty, empty, empty, 'd']
}
*/

// deleted (start of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['b', 'c'] };
diff(left, right);
/*
{
  alphabet: ['b', 'c', undefined]
}
*/

// deleted (end of array)
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['a', 'b'] };
diff(left, right);
/*
{
  alphabet: [empty, empty, undefined]
}
*/

// updated
const left = { alphabet: ['a', 'b', 'c'] };
const right = { alphabet: ['z', 'b', 'c'] };
diff(left, right);
/*
{
  alphabet: ['z', empty, empty]
}
*/

Where empty signifies no difference? (This could be achieved by deleting the items from the array which are the same see)

Closing this for now due to inactivity. Feel free to reopen

@mattphillips I have a use case where the order is not important. It would be perfect if array would be return as arrays.
Anyway we could add some params and use the preserveArray functionalities? I willing to put some time to make it work if necessary

@mattphillips is this library still maintained? If so I have a proposal and am willing to provide a PR.

I don't believe it's possible to add this functionality to the diff method without losing information, but we can certainly add it to all the other, more detailed methods and have them return arrays of what was added and deleted (nothing updates if we ignore the order).

We can take an options argument, or a boolean, to indicate whether to ignore arrays order.
Clean and simple.

+1

A method that returns arrays as arrays would be very useful. There are times when I don't care about
preserving indices yet do not know the names of the property keys to manually convert the values back to an array.

commented

+1

Would be useful to have a diff that works like this regarding to arrays:

  1. If both arrays are equal, there is no diff
  2. If updatedObj is different, just keep updatedObj as a result in the diff (so we kind of treat arrays as scalar values)

An example:

const lhs = {
    bar: {
        a1: ['a', 'b'],
        a2: ['j', 'i'],
        a3: ['x', 'y'],

    },
};
 
const rhs = {
    bar: {
        a1: ['a', 'b'],
        a2: ['j'], // <-- 'i' is removed
        a3: ['x', 'y', 'z'], // 'z' is added
    },
    foo: 'bar', // <-- added new prop
};
 
console.log(diff(lhs, rhs)); // =>
/*
{
    bar: {
        a2: ['j'],
        a3: ['x', 'y', 'z']
    },
    foo: 'bar',
};
*/