3

I have an array that could contain objects. Objects can either be added to it or have a property modified. I want to check if the array has changed at all (could be element(s) added or simply just have one object have a key changed), and then update the DB based on the potential change.

Just wanna know if what I have will cover all cases and/or if there is a better way to do it.

const origArrayCopy = JSON.stringify(origArray);

someFnThatPotentiallyChanges(origArray);

if (origArrayCopy !== JSON.stringify(origArray)) {
    updateDB(arr);
} else {
    console.log('NO DIFF');
}

And here's a jsFiddle I created to test around with https://jsfiddle.net/j4eqwmp6/

Converting the object to a string using stringify should account for deep-nested changes, right? Any insights on this implementation and is there now a more appropriate way to do it?

user2402616
  • 1,434
  • 4
  • 22
  • 38
  • 2
    `JSON.stringify()` might show a change even when nothing has actually changed, because order of elements changed. – Barmar Jan 17 '20 at 14:58
  • See https://stackoverflow.com/questions/201183/how-to-determine-equality-for-two-javascript-objects for more reliable ways to compare objects. – Barmar Jan 17 '20 at 14:59
  • Well there are lots of ways that can fail. – Pointy Jan 17 '20 at 14:59
  • 1
    I think it should be quite rare that a sequence of add/remove operations leaves the array with the same objects in a different order, or for a sequence of object property changes to leave an object with the same properties but in a different iteration order. Since the purpose is to decide whether to do a database update, a few rare false positives is probably better than a less efficient algorithm which checks for unordered equality. My opinion is that `JSON.stringify` should be fine for this task. – kaya3 Jan 17 '20 at 15:26
  • @Barmar may you elaborate a little more on how JSON.stringify() could show a change even if nothing has changed – user2402616 Jan 17 '20 at 15:38
  • 1
    `{"foo": 1, "bar": 2}` and `{"bar": 2, "foo": 1}` are different JSON, but same objects. – Barmar Jan 17 '20 at 15:45
  • 1
    Agree with @kaya3 that it's OK if false positives are acceptable. – Barmar Jan 17 '20 at 15:46
  • Do we know why exactly JS could change order of keys around? – user2402616 Jan 17 '20 at 15:48
  • Seems to be popular opinion that checking for changes in an Object is not a good pattern in https://stackoverflow.com/questions/201183/how-to-determine-equality-for-two-javascript-objects . Why not, and is there any other way to determine if I should hit the DB then? Previously, I was setting flags in the 'someFnThatPotentiallyChanges', but this lead to passing around vars and whatnot. Figured a simple check at the end would be more efficient and with less code – user2402616 Jan 17 '20 at 15:52

1 Answers1

1

Using JSON.stringify is certainly a possibility.

An alternative, is to wrap the object (array) in a proxy, and do that for every nested object as well. Then trap all actions that mutate those objects.

Here is how that could look:

function monitor(obj, cb) {
    if (Object(obj) !== obj) return obj;
    for (let key of Object.keys(obj)) {
        obj[key] = monitor(obj[key], cb);
    }
    return new Proxy(obj, {
        defineProperty(...args) {
            cb();
            return Reflect.defineProperty(...args);
        },
        deleteProperty(...args) {
            cb();
            return Reflect.deleteProperty(...args);
        },
        set(...args) {
            cb();
            return Reflect.set(...args);
        }
    });
};

// Example array
let origArray = [{x: 1}, { child: { y: 1} }];

// Activate the proxy:
let dirty = false;
origArray = monitor(origArray, () => dirty = true);

// Perform a mutation
origArray[1].child.y++;

console.log(dirty); // true
console.log(origArray);
trincot
  • 317,000
  • 35
  • 244
  • 286