4
\$\begingroup\$

I've written a helper for combining objects (deep merge).

It works, and is flexible to add options (like uniq and replace).

Replace should replaces the any matching properties on the object (but spread in any existing properties).

The code below works, but I believe it could be optimized particularly as I could NOT find a way to remove empty keys without the use of JSON.parse(JSON.stringify - it feels wrong to have to crawl back over the entire array to do so.

Using Lodash is not an option.

Examples

DATA = {foo:{i:{z:[1]},x:[1]}}

  • getDeepMerge(DATA, {add:{foo:{q:[1]}}} // {foo:{i:{},x:[1], q:[1]}}
  • getDeepMerge(DATA, {add:{foo:{}}, replace:true} // {foo:{}}
  • getDeepMerge(DATA, {add:{foo:{i:{}}} }, replace:true, removeEmpty:true} // {add:{foo:{i:{}}, x[1]} }

Code

const removeKey = (key, {[key]: _, ...rest}) => rest;

const getNewObj = toAdd => Array.isArray(toAdd) ? getObj(toAdd) : toAdd

const DE_DUPE = (e, i, arr) => arr.indexOf(e) === i

const removeEmptyObj = (o) => {
  let ignores = [null, undefined, ""],
  isNonEmpty = d => !ignores.includes(d) && (typeof(d) !== "object" || Object.keys(d).length)
  return JSON.parse(JSON.stringify(o), function(k, v) {
    if (isNonEmpty(v))
      return v;
  });
}
const getDeepMerge = (ogObj, opts={})=> {
  const { add, uniq=true, replace=false, removeEmpty=false } = opts

  const isObject = obj => obj && typeof obj === 'object';
    return [ogObj, add].reduce((prev, obj) => {
      let _add = {...prev}
      Object.keys(obj).forEach(key => {

        const pVal = _add[key];
        const oVal = obj[key];

        if (Array.isArray(pVal) && Array.isArray(oVal)) {
          _add[key] = pVal.concat(...oVal).filter(uniq ? DE_DUPE : null)
          if(replace){ 
_add[key] = oVal }
          _add = removeEmpty ? removeKey(key, _add) : _add

        }
        else if (isObject(pVal) && isObject(oVal)) {
            _add[key] = getDeepMerge(pVal, {...opts, add:oVal}) 
            if(replace){ 
              _add[key] = oVal
            }
        }
        else {
          _add[key] = oVal
        }
      });
      return removeEmptyObj(_add)
    }, {});
}

Live working demo: https://jsbin.com/yuduvukiqe/1/edit?js,console

\$\endgroup\$
2
  • 3
    \$\begingroup\$ I have tried your code, but it does not output the expected result. Could you please check this? \$\endgroup\$ Commented Aug 19, 2020 at 9:33
  • 1
    \$\begingroup\$ For those in the Close vote queue: Jan's statement is correct - see the output here \$\endgroup\$ Commented Oct 22, 2020 at 15:24

0

Browse other questions tagged or ask your own question.