Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lodash cloneDeepWith to omit undefined

I wrote a customozer function to omit the undefined values of the object with cloneDeepWith. However on the immutable return, it is not digging in recursively. Here is my code:

import { cloneDeepWith, pickBy, omit } from 'lodash';

const obj = {
  a0: true,
  b0: true,
  c0: undefined,
  obj1: {
    a1: true,
    b1: true,
    c1: undefined
  }
};

cloneDeepWith(obj, value => {
    const objWithUndefinedValue = pickBy(obj, (value) => value === undefined);
    const keysWithUndefinedValue = Object.keys(objWithUndefinedValue);
    return omit(obj, keysWithUndefinedValue);
});

However it doesn't recurse after the first return. Is it possible to accomplish this with lodash stock functionality?

like image 240
Noitidart Avatar asked Oct 23 '18 18:10

Noitidart


People also ask

Is null or undefined Lodash?

Lodash helps in working with arrays, strings, objects, numbers, etc. The _. isNil() method is used to check if the value is null or undefined. If the value is nullish then returns true otherwise it returns false.

What is _ cloneDeep?

The _. cloneDeep() method is used to create a deep copy of the value i.e. it recursively clones the value.

How do you check undefined in Lodash?

isUndefined() method is used to find whether the given value is undefined or not. It returns True if the given value is undefined. Otherwise, it returns false.

What does Lodash get do?

get() method in Lodash retrieves the object's value at a specific path. If the value is not present at the object's specific path, it will be resolved as undefined . This method will return the default value if specified in such a case.


2 Answers

As far as I know _.cloneDeepWith() customizer is a bit misleading. The customizer should handle only non object values. If it handles object values, it should also continue the recursion by itself. For example, you can see that as the customizer is called, the cloning is handled by value.cloneNode(true). As you can see only body appears in the console.

const { cloneDeepWith, isObject } = _;

function customizer(value) {
  console.log(value);
  if (_.isElement(value)) {
    return value.cloneNode(true);
  }
}
 
var el = _.cloneDeepWith(document.body, customizer);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

However, if you just customize non object values, and return undefined for object values, which prompts the method to handle them (documented in cloneDeep), it iterates everything:

const { cloneDeepWith, isObject, isUndefined } = _;

const obj = {
  a0: true,
  b0: true,
  c0: undefined,
  obj1: {
    a1: true,
    b1: true,
    c1: undefined
  }
};

const result = cloneDeepWith(obj, v => {
  console.log(v);
  
  return isObject(v) ? undefined : 'custom value';
});

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

To solve your problem, you can use _.transform() recursively to take all values that are not undefined:

const { transform, isObject, isUndefined } = _;

const obj = {
  a0: true,
  b0: true,
  c0: undefined,
  obj1: {
    a1: true,
    b1: true,
    c1: undefined
  }
};

const cloneDeepWithoutUndefined = (obj) =>
  transform(obj, (r, v, k) => {
    if(isUndefined(v)) return;
    r[k] = isObject(v) ? cloneDeepWithoutUndefined(v) : v;
  });
  
const result = cloneDeepWithoutUndefined(obj);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
like image 197
Ori Drori Avatar answered Sep 25 '22 23:09

Ori Drori


This can be solved by using recursively lodash transform method via:

const obj = { a0: true, b0: true, c0: undefined, obj1: { a1: true, b1: true, c1: undefined } };

const cloneMe = (obj) => _.transform(obj, (r, v, k) => 
  _.isUndefined(v) ? null : _.isObject(v) ? r[k] = cloneMe(v) : r[k] = v, {})

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

You could also do this in ES6 only via Object.entries & reduce:

const obj = { a0: true, b0: true, c0: undefined, obj1: { a1: true, b1: true, c1: undefined } };

const cloneMe = (obj) => {
   return Object.entries(obj).filter(([k,v]) => 
      v != undefined).reduce((r,[k,v]) => {
        r[k] = (v instanceof Object) ? cloneMe(v) : v
        return r
   },{})
}

console.log(cloneMe(obj))

You can additionally extend the check for object if instance of Object is not sufficient etc.

like image 43
Akrion Avatar answered Sep 24 '22 23:09

Akrion