Search code examples
javascriptlodash

Versions of defaultDeep and defaults that override undefined and null values


I'm looking for versions of Lodash's defaults and defaultsDeep that override null values in addition to undefined. I've looked at the source for defaultsDeep but couldn't figure out a simple way to get it working. I'd rather use a simple solution, possibly based on Lodash, than roll my own.


Solution

  • You can solve this by using lodash#mergeWith to merge objects with lodash#isNil as the identifying condition to determine if a variable is null or undefined.

    // Note: if you change `_.isNil` to `_.isUndefined`
    // then you'd get the `_.defaults()` normal behavior
    const nilMerge = (a, b) => _.isNil(a)? b: a;
    
    const nilMergeDeep = (a, b) => (_.isObject(a) && !_.isArray(a))
      // recursively merge objects with nilMergeDeep customizer
      ? _.mergeWith({}, a, b, nilMergeDeep) 
      // let's use our default customizer
      : nilMerge(a, b);
    
    // defaults not deep with null/undefined
    const result1 = _.mergeWith({}, o1, o2, o3, nilMerge);
    
    // defaults deep with null/undefined
    const result2 = _.mergeWith({}, o1, o2, o3, nilMergeDeep);
    

    const o1 = {
      a: 1,
      b: 2,
      c: 3,
      d: null,
      x: {
        x1: 1,
        x2: 2,
        x3: null
      },
      z: null
    };
    
    const o2 = {
      a: 9999,
      d: 4,
      e: null,
      f: 123,
      x: {
        x3: 3,
        x4: null
      },
      z: ['a', 'b']
    };
    
    const o3 = {
      b: 9999,
      e: 5,
      f: 2,
      g: 234,
      x: {
        x4: 4,
        x5: 5,
        x6: ['hi', 'there']
      },
      z: ['c']
    };
    
    // Note: if you change `_.isNil` to `_.isUndefined`
    // then you'd get the `_.defaults()` normal behavior
    const nilMerge = (a, b) => _.isNil(a)? b: a;
    
    const nilMergeDeep = (a, b) => (_.isObject(a) && !_.isArray(a))
      // recursively merge objects with nilMergeDeep customizer
      ? _.mergeWith({}, a, b, nilMergeDeep) 
      // let's use our default customizer
      : nilMerge(a, b);
    
    // defaults not deep with null/undefined
    const result1 = _.mergeWith({}, o1, o2, o3, nilMerge);
    
    // defaults deep with null/undefined
    const result2 = _.mergeWith({}, o1, o2, o3, nilMergeDeep);
    
    console.log('defaults not deep with null/undefined');
    console.log(result1);
    
    console.log('defaults deep with null/undefined');
    console.log(result2);
    .as-console-wrapper{min-height:100%;top:0}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>