skip to Main Content

I have two setting config

const defaultSetting = {
  level1: {
    setting1: "defaultValue1",
    nested: true,
    home: "africa"
  },
  level2: {
    setting2: "defaultValue3"
  }
};

const initialSetting = {
  level1: {
    setting1: "initialValue1", // This value will override defaultSetting's value
    nested: false // This value will override defaultSetting's value
  },
  level2: {
    nested: true // This value will override defaultSetting's value, adding a new property not in defaultSetting's level2
  }
};

What I am trying to achieve is that I want to merge initialSetting into defaultSetting, such that:

  1. The structure of defaultSetting dictates the final structure.
  2. Values from initialSetting overwrite those in defaultSetting where they
    exist.
  3. If initialSetting contains keys not present in
    defaultSetting, they are not included in the final object.
  4. If defaultSetting contains keys not present in initialSetting, their
    default values are retained.

I want to strictly adhere to the structure of defaultSetting while selectively pulling in values from initialSetting.

I have this code

const mergeTwoObjects = (obj11: NestedObject | undefined, obj2: NestedObject): unknown => {
 const result: NestedObject = {};
  const obj1 = obj11 ?? {};

  (Object.keys(obj2) as Array<keyof typeof obj2>).forEach((key) => {
    if (Object.prototype.hasOwnProperty.call(obj1, key)) {
      if (typeof obj1[key] === "object" && typeof obj2[key] === "object") {
        result[key] = mergeTwoObjects(
          obj1[key] as NestedObject,
          obj2[key] as NestedObject,
        ) as NestedObject;
      } else {
        result[key] = obj1[key];
      }
    } else {
      result[key] = obj2[key];
    }
  });
  return result;
};

But the requirement came as that I can not build my function from scratch rather than that I should be using lodash and make code simple without many loops

I tried this but it did not work

const defaultSetting = {
  level1: {
    setting1: "defaultValue1",
    nested: true,
    home: "africa"
  },
  level2: {
    setting2: "defaultValue3"
  }
};

const initialSetting = {
  level1: {
    setting1: "initialValue1", // This value will override defaultSetting's value
    nested: false // This value will override defaultSetting's value
  },
  level2: {
    nested: true // This value will override defaultSetting's value, adding a new property not in defaultSetting's level2
  }
};

function mergeSettings(defaultSetting, initialSetting) {
  return _.mergeWith({}, defaultSetting, initialSetting, (objValue, srcValue, key, object, source, stack) => {
      if (_.isObject(objValue) && _.isObject(srcValue)) {
          // Return undefined to let _.mergeWith handle the deep merge of these objects
          return undefined;
        }
    // If srcValue is undefined, use objValue (from defaultSetting)
    return srcValue === undefined ? objValue : srcValue;
  });
}

const finalSetting = mergeSettings(defaultSetting, initialSetting);

console.log(finalSetting);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

I am new to Lodash, can anyone please help me achieve this

What I want to get is:

{
  "level1": {
    "setting1": "initialValue1",
    "nested": false,
    "home": "africa"
  },
  "level2": {
    "setting2": "defaultValue3",
  }
}

2

Answers


  1. This is not possible in the exact sense with lodash methods. The closest you can do it nullify all values coming from object not present in the initial object. Otherwise use a combination of mergeWith and omit. An example with missing values as null:

    const defaultSetting = {
      level1: {
        setting1: "defaultValue1",
        nested: true,
        home: "africa"
      },
      level2: {
        setting2: "defaultValue3"
      }
    };
    
    const initialSetting = {
      level1: {
        setting1: "initialValue1", // This value will override defaultSetting's value
        nested: false // This value will override defaultSetting's value
      },
      level2: {
        nested: true // This value will override defaultSetting's value, adding a new property not in defaultSetting's level2
      }
    };
    
    function customiser(objValue, srcValue) {
      if (_.isNil(objValue))
        return null;
    }
    
    const finalSetting = _.mergeWith(defaultSetting, initialSetting, customiser);
    console.log(finalSetting);
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
    Login or Signup to reply.
  2. If I’m not overlooking something, this relatively simple recursive function merge should do the trick (no library required):

    const defaultSetting = {
      level1: {
        setting1: "defaultValue1",
        nested: true,
        home: "africa"
      },
      level2: {
        setting2: "defaultValue3"
      }
    };
    
    const initialSetting = {
      level1: {
        setting1: "initialValue1", // This value will override defaultSetting's value
        nested: false // This value will override defaultSetting's value
      },
      level2: {
        nested: true // This value will override defaultSetting's value, adding a new property not in defaultSetting's level2
      }
    };
    
    const merge = (o, m) => Object.entries(o).reduce((acc, [k, v]) => {
      acc[k] = typeof m[k] === 'object' 
        ? merge(v, m[k]) : 
          typeof m[k] === 'undefined' 
            ? v 
            : m[k];
      return acc;
    }, {});
    
    const finalSetting = merge(defaultSetting, initialSetting);
    
    console.log(finalSetting);
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search