Reputation: 1035
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
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
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
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
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
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
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
Reputation: 120
let words = [null, undefined, 'cheaters', 'pan', 'ear', 'era']
console.log(words.filter(word => word != null));
Upvotes: 3
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
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 NaN
s (typeof(NaN)
returns "number")
Upvotes: 1
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