4

I'd like to merge two similar but not identical objects and override null values in one of them, if such exist. For example I'd have these two objects:

const obj1 = {
    a: 1,
    b: '',
    c: [],
    d: null
}

const obj2 = {
    a: 2,
    b: null,
    d: 1
}

And the effect of merge should be:

const objMerged = {
    a: 2,
    b: '',
    c: [],
    d: 1
}

In other words, the most important source of data in the merged object is obj2 but it lacks some properties from obj1, so they need to be copied and also some of the obj2 values are null so they should be taken from obj1 as well.

EDIT I tried:

_.extend({}, obj1, obj2) 

and

Object.assign({}, obj1, obj2)
Hubert Kubiak
  • 607
  • 1
  • 7
  • 30

8 Answers8

14

You could also mix and match with ES6 destructuring and lodash _.omitBy:

const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }

const result = {..._.omitBy(obj1, _.isNull), ..._.omitBy(obj2, _.isNull)}

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

You could also do it with ES6 only like this:

const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }

let omitNull = obj => {
  Object.keys(obj).filter(k => obj[k] === null).forEach(k => delete(obj[k]))
  return obj
}

const result = { ...omitNull(obj1), ...omitNull(obj2) }

console.log(result)
Akrion
  • 18,117
  • 1
  • 34
  • 54
5

You can use _.mergeWith(), and in the merge callback only take the 2nd value if it's not null:

const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }

const result = _.mergeWith({}, obj1, obj2, (o, s) => _.isNull(s) ? o : s)

console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
5

To add to this list of good answers, here's a recursive solution that will work with nested structures.

This example will merge the common properties of the dst object to the src object in all levels of nesting, leaving any properties that are not common intact.

const merge = (dst, src) => {
  Object.keys(src).forEach((key) => {
    if (!dst[key]) {
      dst[key] = src[key];
    } else if (typeof src[key] === 'object' && src[key] !== null && typeof dst[key] === 'object' && dst[key] !== null) {
      merge(dst[key], src[key]);
    }
  });
},

/* Usage: */

src = {
  prop1: '1',
  prop2: {
    val: 2,
  }
},
dst = {
  prop1: null,
  prop2: {
    val: null,
  },
  prop3: null,
};

merge(dst, src);
console.log(dst);
Andrew Rooney
  • 169
  • 2
  • 6
3

Here is a pure JS based solution:

Iterate through the first object to replace values from second object, then add the additional values from the second object.

const obj1 = {
    a: 1,
    b: '',
    c: [],
    d: null
}

const obj2 = {
    a: 2,
    b: null,
    d: 1
}

function mergeObjs(obj1, obj2){
  const merged = {}
  keys1 = Object.keys(obj1);
  keys1.forEach(k1 => {
     merged[k1] = obj2[k1] || obj1[k1]; // replace values from 2nd object, if any
  })
  Object.keys(obj2).forEach(k2 => {
     if (!keys1.includes(k2)) merged[k2] = obj[k2]; // add additional properties from second object, if any
  })
  return merged
}


console.log(mergeObjs(obj1, obj2))
Dani Vijay
  • 2,188
  • 2
  • 22
  • 37
2

Using Lodash by create() and omitBy()

const obj1 = {"a":1,"b":"","c":[],"d":null}

const obj2 = {"a":2,"b":null,"d":1}

const objMerged = _.create(
  _.omitBy(obj1, _.isNull),
  _.omitBy(obj2, _.isNull)
)

console.log(objMerged)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
User863
  • 19,346
  • 2
  • 17
  • 41
1

If you're interested in only the first level of the two objects you could do something like this:

const obj1 = {
  a: 1,
  b: '',
  c: [],
  d: null
}

const obj2 = {
  a: 2,
  b: null,
  d: 1
}

const merged = Object.keys(obj1).concat(Object.keys(obj2)) // create an array that contains the keys of the two objects.
  .filter((k, i, arr) => arr.indexOf(k) === i) // remove duplicate keys
  .reduce((a, c) => {
    a[c] = obj1[c] !== null ? obj1[c] : obj2[c];
    return a;
  }, {});

console.log(merged);

This example only check for null values, you should probably extend it to check for others like undefined, empty strings, etc.

Titus
  • 22,031
  • 1
  • 23
  • 33
1

You did it the good way using Object.assign, just remove what you don't want right before

Object.keys(obj1).forEach( k => {
    if ( obj1[k] //write the condition you want
        delete obj1[k]
});
farvilain
  • 2,552
  • 2
  • 13
  • 24
1
var objMerged  = {};
for (var kobj1 in obj1) {
    for (var kobj2 in obj2) {
        if (obj1[kobj1] == null && obj2[kobj1] != null)
            objMerged[kobj1] = obj2[kobj1];
        else if (obj2[kobj2] == null && obj1[kobj2] != null)
            objMerged[kobj2] = obj1[kobj2];

    }
}

//Print objMerged to display