Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an ES6 function that will return an object containing property changes?

ES6 has a lot of functions including assign and others. But is there a method to get a list of properties that are different from one object to the next?

For example, if I have a component with two states. The default state has 100 properties that define it. State two there are only 10 properties that change. Let's say I get 2 objects containing all 100 properties. I want to create object 3 that has only the 10 properties that have changed (actually not only the properties that changed but the properties on the second object - see update).

The second object keeps all its unique properties and overrides the properties in the first.

I thought Object.assign() might do this but I don't think so.

    var object = {name:Fred, age: 20, weight: 100};
    var object2 = {name:Fred, age: 21, weight: 120};

    function getChangesFromObjectTwo(object1, object2) {

        return object;
    }

    // returns {age:21, weight: 120};
    var changes = getChangesFromObjectTwo(object, object2);

UPDATE:
Great answers. I wasn't specific enough... If object2 has additional properties they should show on the returned object.

    var object = {name:Fred, age: 20, weight: 100};
    var object2 = {name:Fred, age: 21, weight: 120, height: 70};

    function getChangesFromObjectTwo(object1, object2) {

        return object;
    }

    // returns {age:21, weight: 120, height: 70};
    var changes = getChangesFromObjectTwo(object, object2);
like image 385
1.21 gigawatts Avatar asked May 25 '20 06:05

1.21 gigawatts


4 Answers

Doesn't have to be ES6, but you can implement it like this:

var object = {name: 'Fred', age: 20, weight: 100};
var object2 = {name: 'Fred', age: 21, weight: 120, height: 70};

function getChangesFromObjectTwo(source, target) {
  return Object.fromEntries(Object.entries({...source, ...target})
        .filter(([key, value]) => !Object.is(source[key], value)));
}

// returns {age:21, weight: 120};
var changes = getChangesFromObjectTwo(object, object2);
console.log(changes);

Added properties also included

P.S. Using Object.is to bypass NaN problem

like image 53
Hao Wu Avatar answered Nov 15 '22 07:11

Hao Wu


Assuming that keys are identical in both objects o1 and o2, you can just use Object.keys() with a a reduce() operation:

Object.keys(o2).reduce((a, k) => (o1[k] !== o2[k] && (a[k] = o2[k]), a), {});

Full snippet:

const object1 = {name:'Fred', age: 20, weight: 100};
const object2 = {name:'Fred', age: 21, weight: 120};

function getChanges(o1, o2) {
  return Object.keys(o2)
               .reduce((a, k) => (o1[k] !== o2[k] && (a[k] = o2[k]), a), {});
}

console.log(getChanges(object1, object2));

Or if you're working in an environment that supports Object.entries(), you can avoid a couple of lookups:

Object.entries(o2).reduce((a, [k, v]) => (o1[k] !== v && (a[k] = v), a), {});

Full snippet:

const object1 = {name:'Fred', age: 20, weight: 100};
const object2 = {name:'Fred', age: 21, weight: 120};

function getChanges(o1, o2) {
  return Object.entries(o2)
               .reduce((a, [k, v]) => (o1[k] !== v && (a[k] = v), a), {});
}

console.log(getChanges(object1, object2));
like image 28
Robby Cornelissen Avatar answered Nov 15 '22 06:11

Robby Cornelissen


I believe Proxy is the tool you are looking for.

In your case, if you have

const defaultObject = {name: "Fred", age: 20, weight: 100}; // 100+ properties
var clonedObject = {...defaultObject}; // shallow copy

you can create monitor object and proxy object for them:

var monitor = {};
const proxy = new Proxy(clonedObject, {
  set: function(obj, prop, value) {
    obj[prop] = value;
    if(defaultObject[prop]!==value) monitor[prop] = value;
    else delete monitor[prop];
  }
});

Now if you change anything in proxy...

proxy.age = 21;
proxy.weight = 120;
proxy.age = 20; // restore default
proxy.height = 70; // new value

...you can see the changes in monitor

console.log(monitor); // weight: 120, height: 70
console.log(clonedObject); // proxy passes the changes to clonedObject

Updated snippet you requested:

const defaultObject = {name: "Fred", age: 20, weight: 100};
var clonedObject = {...defaultObject}; // shallow copy


var monitor = {};
const proxy = new Proxy(clonedObject, {
  set: function(obj, prop, value) {
    obj[prop] = value;
    if(defaultObject[prop]!==value) monitor[prop] = value;
    else delete monitor[prop];
  }
});


proxy.age = 21;
proxy.weight = 120;
proxy.age = 20; // restore default
proxy.height = 70; // new value


console.log(monitor); // weight: 120, height: 70
console.log(clonedObject);
like image 29
Jan Turoň Avatar answered Nov 15 '22 06:11

Jan Turoň


Object.assign does the opposite of what you want: it modifies an object based on an object listing changes.

There's no built-in function that does it, but you can easily implement your own.

For a single level depth observation, it should be enough to:

function getChangesFromObjectTwo(obj1, obj2){
  //Using Set to create an unique list
  return [...new Set([
      ...Reflect.ownKeys(obj1),
      ...Reflect.ownKeys(obj2)
    ])]
    .map(k => [k, obj1[k], obj2[k]])
    .filter(([, v1, v2]) => v1 !== v2)
    .reduce((acc, [k, , v]) => (
      acc[k] = v,
      acc
    ), {})
}

var object = {name:"Fred", age:20, weight: 100};
var object2 = {name:"Fred", age:21, weight: 120, height: 70};

console.log(getChangesFromObjectTwo(object, object2));
like image 36
FZs Avatar answered Nov 15 '22 08:11

FZs