Javascript assign only intersection of source to target object

I have two object, and I want to update the target object values from source, but only the values which exists in the target already.

const target = {
    a: 1,
    b: 2,
    c: {
        c_a: 3,
        c_b: 4,
    }
}

const source = {
    a: 10,
    c: {
        c_a: 30,
        c_d: 50,
    },
    d: 60
}

const result = assignOnlyIntersection(target, source);

JSON.stringify(result)

// {
//    a: 10,
//    b: 2,
//    c: {
//        c_a: 30,
//        c_b: 4
//    }
// }

Is there a good way in ES6 for this? If not, does lodash has a feature for this? If not how can it be solved in a nice way?

Upvotes: 4

Views: 727

Answers (5)

Lindauson
Lindauson

Reputation: 3451

You can combine Array.reduce() and Object.keys() ES6 features to achieve this:

let target = {
a: 1,
b: 2,
c: {
    c_a: 3,
    c_b: 4,
}
}

let source = {
a: 10,
c: {
    c_a: 30,
    c_d: 50,
},
d: 60
}

const assignOnlyIntersection = (sourceObj, targetObj) => {
return Object.keys(targetObj).reduce((intersection, key) => { 
    intersection[key] = (typeof targetObj[key] === 'object') ? assignOnlyIntersection(sourceObj[key], targetObj[key]) : sourceObj[key] || targetObj[key];
    return intersection; 
}, {})
}

target = assignOnlyIntersection(source, target);

console.log(target);

Upvotes: 0

Jayesbe
Jayesbe

Reputation: 103

another ES6 approach

non-mutative:

const intersector = (target = {}, source = {}) => {
  return Object.keys(source).reduce((acc, prop) => {
    if (target.hasOwnProperty(prop)) {
      if (typeof source[prop] === 'object' && source[prop] !== null) {
        acc[prop] = intersector(target[prop], source[prop]);
      }
      else {
        acc[prop] = source[prop];
      } 
    }
    return acc;
  }, {});
}

mutative:

const intersector = (target = {}, source = {}) => {
  return Object.keys(source).reduce((acc, prop) => {
    if (target.hasOwnProperty(prop)) {
      if (typeof source[prop] === 'object' && source[prop] !== null) {
        acc[prop] = intersector(target[prop], source[prop]);
      }
      else {
        acc[prop] = source[prop];
      }
      target[prop] = acc[prop];
    }
    return acc;
  }, target);
};

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386756

You could take a recursive approach by checking for objects.

function deepAssign(target, source) {
    Object.keys(source).forEach(function (k) {
        var objectCount = [source, target]
                .map(o => o[k])
                .map(o => o && typeof o === 'object')
                .map(Boolean)
                .reduce((a, b) => a + b)

        if (!(k in target) || objectCount === 1) {
            return;
        }
        if (objectCount === 2) {
            return deepAssign(target[k], source[k]);
        }
        target[k] = source[k];
    });
    return target;
}

var source = { a: 10, c: { c_a: 30, c_d: 50, }, d: 60 },
    target = { a: 1, b: 2, c: { c_a: 3, c_b: 4, } };

console.log(deepAssign(target, source));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Bergi
Bergi

Reputation: 665256

A simple recursive function will do:

function assignOnlyIntersection(target, source) {
    if (Object(target) !== target || Object(source) !== source)
        return source;
    for (const p in source)
        if (p in target)
            target[p] = assignOnlyIntersection(target[p], source[p]);
    return target;
}

Upvotes: 3

Martin Adámek
Martin Adámek

Reputation: 18429

You need to iterate through keys of target object, check if there is same key on source object and if so, replace the value. You also need recursion if you find an object on the way.

Something like this could do the job:

const target = { a: 1, b: 2, c: { c_a: 3, c_b: 4 }};
const source = { a: 10, c: { c_a: 30, c_d: 50 }, d: 60 };

function assignOnlyIntersection(t, s) {
    Object
        .keys(s)
        .filter(k => k in t)
        .forEach(k => {
           if (typeof t[k] === 'object') {
               assignOnlyIntersection(t[k], s[k]);
           } else {
               t[k] = s[k];
           }
    });
    
    return t;
}

const result = assignOnlyIntersection(target, source);

console.log(JSON.stringify(result));

Also note that if you want to support replacing with falsy values like 0 or '', you will need to check for existence with typeof t[k] !== 'undefined'.

As @ghybs noted, if you want to also support replacing with undefined values, you will need to check via in operator k in t instead.

Upvotes: 0

Related Questions