tutorialfeed
tutorialfeed

Reputation: 1035

Checking 'undefined' or 'null' of any Object

I am working on Angular project and time to time I used to have check undefined or null over Object or it's properties. Normally I use lodash _.isUndefined() see example below:

this.selectedItem.filter(i => {
    if(_.isUndefined(i.id)) {
      this.selectedItem.pop();
    }
})

I couldn't see any problem with it. But I had discussion with my colleague during review of above code. He was telling me that if i gets undefined before the if statement then it will throw the exception. Instead he suggested me to always check i or i.id like this:

if(!!i && !!i.id) {
      this.selectedItem.pop();
}

I am convinced what he was trying to say unlike his way of checking undefined in above code. But then I was thinking what is the purpose of lodash _.isUndefined?

Could anyone please let me know what is the best or clean way to do it. Because for me !!i && !!i.id is not readable at all.

Many thanks in advance.

Upvotes: 31

Views: 63814

Answers (10)

Ori Drori
Ori Drori

Reputation: 192122

You can use _.isNil() to detect undefined or null. Since you're using Array.filter(), you want to return the results of !_.isNil(). Since i is supposed to be an object, you can use !_.isNil(i && i.id).

Note: you are using Array.filter() as Array.forEach(). The callback of Array.filter() should return a boolean, and the result of the filter is a new array.

const selectedItem = [
  undefined,
  {},
  { id: 5 },
  undefined,
  { id: 7 },
];

const result = selectedItem.filter(i => !_.isNil(i?.id));

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

You can also use _.reject() and save the need to add !:

const selectedItem = [
  undefined,
  {},
  { id: 5 },
  undefined,
  { id: 7 },
];

const result = _.reject(selectedItem, i => _.isNil(i?.id));

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

Upvotes: 37

alfonsodev
alfonsodev

Reputation: 2754

Javascript has now (Chrome 80, Safari 13.4) Optional chaining (?.)

The optional chaining operator (?.) permits reading the value of a property located deep within a chain of connected objects without having to expressly validate that each reference in the chain is valid.

This means you could check for id without causing an exception in case i is undefined

this.selectedItem.filter(i => {
  if(i?.id) {
    this.selectedItem.pop();
  }
})

Or since you are using filter, you can check test this live on the filter documentation.

const words = ['spray', undefined, 'elite', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word?.length > 6);

console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]

Additionally, also worth to mention, as @Koushik Chatterjee's answer points, lodash _.get allows you to describe a path to a deep property safely, and even give a default value in case it doesn't exist.

var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, ['a', '0', 'b', 'c']);
// => 3

_.get(object, 'a.b.c', 'default');
// => 'default'

Upvotes: 1

Amit Joki
Amit Joki

Reputation: 59252

Your friend is right. When you do, _.isUndefined(i.id) you're assuming i to not to be undefined. You're assuming i is an object which will have an id property which you're checking if it is falsey or not.

What happens when the variable i itself is undefined? So you will end up undefined.id which is an error. Therefore you could simply do this

if(i && i.id) {
  // You're good to go
}

The above will check for all falsey values, 0 and "" included. So if you want to be very specific, then you'll have to check the types of both using typeof operator.

Upvotes: 2

Ram
Ram

Reputation: 144699

Referring to a variable which has undefined as it's value won't throw any error. You get a ReferenceError for referring to variable that is not defined:

> i
Uncaught ReferenceError: i is not defined

If you pass a not-defined variable to a function a ReferenceError is thrown and the function won't be executed.

> _.isUndefined(i)
Uncaught ReferenceError: i is not defined

typeof operator should be used for safely checking whether a variable is defined or not:

> typeof i
'undefined'

In your code the i is defined (it's a function argument) so by referring to it you won't get any ReferenceError. The code will throw a TypeError when i is defined, has undefined value and you are treating it as an object:

> var i = undefined; i.id
Uncaught TypeError: Cannot read property 'id' of undefined

Upvotes: 3

Akrion
Akrion

Reputation: 18525

Another more "lodashy" approach would be to use _.conforms. The readability is much better in my opinion and you get access directly to id so no problems with undefined before that:

const items = [
  undefined,
  { id: null},
  { id: 5 },
  { id: "4" },
  { id: undefined },
  undefined,
  { id: 0 },
  { id: 7 },
  { id: () => 3 }
];

const numbersOnly = _.filter(items, _.conforms({'id': _.isNumber}));
console.log('numbers', numbersOnly);

const allDefined = _.filter(items, _.conforms({'id': _.negate(_.isUndefined)}));
console.log('defined', allDefined);

const stringsOnly = _.filter(items, _.conforms({'id': _.isString}));
console.log('strings', stringsOnly);

const functionsOnly = _.filter(items, _.conforms({'id': _.isFunction}));
console.log('functions', functionsOnly);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Upvotes: 1

Koushik Chatterjee
Koushik Chatterjee

Reputation: 4175

You can use lodash#get (it will handle if the root is value null or undefined), and then compare the output with null using == or !=, instead of using === or !==. if the output is null or undefined then comparing with == will be true and != will be false.

const selectedItem = [
  undefined,
  {},
  {id: null},
  { id: 5 },
  undefined,
  { id: 0 },
  { id: 7 },
];

const res = selectedItem.filter(a => _.get(a, 'id') != null);

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

using lodash#get you can checkfor any nested level for its existence with out subsequent && like a && a.b && a.b.c && a.b.c.d (in case of d) look at this answer of mine for nested level checking with lodash#get

Also you can use _.isNill instead of comparing with null will == or !=

Upvotes: 1

Anouar
Anouar

Reputation: 120

let words = [null, undefined, 'cheaters', 'pan', 'ear', 'era']

console.log(words.filter(word => word != null));

Upvotes: 3

Nina Scholz
Nina Scholz

Reputation: 386650

You could check for i and if it is not truthy or if the property is undefined or null, then do something.

if (!i || i.id === undefined || i.id === null) {
    this.selectedItem.pop();
}

Upvotes: 3

Elias
Elias

Reputation: 4142

May I suggest checking for if(typeof(element) == "number" && element) {} In this case the typeof() part catches any non numbers and the element part should catch any NaNs (typeof(NaN) returns "number")

Upvotes: 1

Daniel A. White
Daniel A. White

Reputation: 190947

Use typeof i.id === 'undefined' to check for undefined and i.id === null to check for null.

You could write your own helper functions to wrap any logic like what LoDash has. The condition with !!i && !!i.id is only looking for falsy values (empty string, 0, etc), not only null or undefined.

Upvotes: 4

Related Questions