BennyTicklez
BennyTicklez

Reputation: 157

Compare two deep nested object of objects and return only the differences in javascript / lodash

I have 2 deep nested object of objects. One is a "base" object and the other is a modified one.

I essentially want to "delete" the base object from the modified object and return an object that only contains different data. Here is an example:

Base object:

baseObj : {
    image: {
        width: '100%',
        paddingTop: '0px',
        paddingRight: '0px',
        paddingBottom: '0px',
        paddingLeft: '0px'
    },
    wrap: {
        marginTop: '0px',
        marginRight: '0px',
        marginBottom: '0px',
        marginLeft: '0px'
    }
}

Object with modified data

mergedObject : {
    image: {
        width: '100%',  
        paddingTop: '0px',
        paddingRight: '24px', // modified value
        paddingBottom: '24px', // modified value
        paddingLeft: '0px'
    },
    wrap: {
        height: '100px', // new value
        marginTop: '24px', // modified value
        marginRight: '0px',
        marginBottom: '0px',
        marginLeft: '24px' // modified value
    }
}

I would like an object that is returned like this:

diffObject : {
    image: {
        paddingRight: '24px',
        paddingBottom: '24px',
    },
    wrap: {
        height: '100px',
        marginTop: '24px',
        marginLeft: '24px'
    }
}

The nesting could go a little deeper so it needs to be dynamic. Is there a way using lodash or some other library?

Upvotes: 0

Views: 4265

Answers (2)

ryeballar
ryeballar

Reputation: 30088

To solve this problem, we must:

  • Create a function that traverses each node in the object recursively. We can achieve this by using the reduce function when traversing the source object.
  • The reduce callback function would first check if the source value or it's other value counter part are both objects. If they are, we'll use these values as arguments to recursively get the difference of both these objects. Otherwise, if they are different, then we'll assign the resulting value from the other value.
  • Lastly, the reduce function accepts a third argument which is used as the initial value for the accumulated of the reduce function. We assign the third argument with key-values from the other object that aren't found in the source object. This is the simplest way to get the newly introduced values from the other object towards the resulting object.

function differenceObjectDeep(source, other) {
  return _.reduce(source, function(result, value, key) {
    if (_.isObject(value) && _.isObject(other[key])) {
      result[key] = differenceObjectDeep(
        value,
        other[key]
      );
    } else if (!_.isEqual(value, other[key])) {
      result[key] = other[key];
    }
    return result;
  }, _.omit(other, _.keys(source)));
}

data.diffObject = differenceObjectDeep(
  data.baseObj,
  data.mergedObject
);

var data = {
  baseObj: {
    image: {
      width: '100%',
      paddingTop: '0px',
      paddingRight: '0px',
      paddingBottom: '0px',
      paddingLeft: '0px'
    },
    wrap: {
      marginTop: '0px',
      marginRight: '0px',
      marginBottom: '0px',
      marginLeft: '0px'
    }
  },
  mergedObject: {
    image: {
      width: '100%',
      paddingTop: '0px',
      paddingRight: '24px', // modified value
      paddingBottom: '24px', // modified value
      paddingLeft: '0px'
    },
    wrap: {
      height: '100px', // new value
      marginTop: '24px', // modified value
      marginRight: '0px',
      marginBottom: '0px',
      marginLeft: '24px' // modified value
    }
  }
};

function differenceObjectDeep(source, other) {
  return _.reduce(source, function(result, value, key) {
    if (_.isObject(value) && _.isObject(other[key])) {
      result[key] = differenceObjectDeep(
        value,
        other[key]
      );
    } else if (!_.isEqual(value, other[key])) {
      result[key] = other[key];
    }
    return result;
  }, _.omit(other, _.keys(source)));
}

data.diffObject = differenceObjectDeep(
  data.baseObj,
  data.mergedObject
);

console.log(data.diffObject);
body>div {
  min-height: 100%;
  top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Upvotes: 2

Alaksandar Jesus Gene
Alaksandar Jesus Gene

Reputation: 6883

you could use recursive function to find the last object.

var checkIfObject = function(item){ //recursive function
_.each(item, function(v, k){
if(_.isObject(v)){
previousObject = k;
previousResult = v;
return checkIfObject(v);
}else{
    finalChildObjects[previousObject] = previousResult;
}
})
return finalChildObjects
}

/*Initialize Values for recursive function*/
var finalChildObjects = {};
var previousObject = '';
var previousResult = '';
baseObjectFinalTurn = checkIfObject(baseObj);
/*Initialize Values for recursive function*/
var finalChildObjects = {};
var previousObject = '';
var previousResult = '';
mergedObjectFinalTurn = checkIfObject(mergedObject);
console.clear();


var difference = {};
_.each(baseObjectFinalTurn, function(v, k){
    if(mergedObjectFinalTurn[k]){
    difference[k] = _.reduce(mergedObjectFinalTurn[k],function(result, value, key){
    if(baseObjectFinalTurn[k][key] != mergedObjectFinalTurn[k][key]){
    result[key] =  value;
    }
    
    return result
    }, {})
    
  }
  else{
  difference[k] = {};
  }
})

console.log(difference)

You can verify the result from this jsfiddle link

Note: you need to adjust the result based on your requirement in reduce. This result is made to match your output.

Upvotes: 0

Related Questions