Check if sub-object exists before using it - javascript

In order to prevent using an object's value that doesn't exist (which would throw an error), I usually do something like this:
if(apple.details){
// do something with apple.details
}
and it normally works fine. But if it's about a "object's object's value", like apple.details.price, that doesn't work, because if not even .details exists, the if() would throw an error.
What can I do or is there generally a better way to do this?

You can do chain:
if (apple && apple.details && apple.details.price) { ... }
But it's not convenient if the chain is long. Instead you can use lodash.get method
With lodash:
if (get(apple, 'details.price')) { ... }

Since 2020:
The best way to solve this is using the Optional Chaining Operator
if(apple?.details) // Undefined if apple does not exist
Suggestion of lodash is no longer required when using runtime compatible with ES2020 i.e. Most browsers today & node>14.5

You may try solution of #Sphinx
I would also suggest _.get(apple, 'details.price') of lodash, but surely it is not worth to include whole library for simple project or few tasks.
_.get() function also prevents from throwing error, when even apple variable is not defined (undefined)

You would have to check each parent object prior to checking the nested child object, i.e., if (a && a.b && a.b.c) {}
If you're using LoDash, you can use the _.get function with an optional default:
let obj = {'a': {'b': {'c': 42} } };
let result = _.get(obj, 'a.b.c'); // 42
let result2 = _.get(obj, 'a.b.d'); // undefined
let result3 = _.get(obj, 'a.c.d', null); // null
If you're not using LoDash, you can implement a simplified version of _.get as shown in this answer.

Related

How can I use optional chaining with arrays and functions?

I'm trying to use optional chaining with an array instead of an object but not sure how to do that:
Here's what I'm trying to do myArray.filter(x => x.testKey === myTestKey)?[0].
Also trying similar thing with a function:
let x = {a: () => {}, b: null}
console.log(x?b());
But it's giving a similar error - how can I use optional chaining with an array or a function?
You need to put a . after the ? to use optional chaining:
myArray.filter(x => x.testKey === myTestKey)?.[0]
Playground link
Using just the ? alone makes the compiler think you're trying to use the conditional operator (and then it throws an error since it doesn't see a : later)
Optional chaining isn't just a TypeScript thing - it is a finished proposal in plain JavaScript too.
It can be used with bracket notation like above, but it can also be used with dot notation property access:
const obj = {
prop2: {
nested2: 'val2'
}
};
console.log(
obj.prop1?.nested1,
obj.prop2?.nested2
);
And with function calls:
const obj = {
fn2: () => console.log('fn2 running')
};
obj.fn1?.();
obj.fn2?.();
Just found it after a little searching on the what's new page on official documentation
The right way to do it with array is to add . after ?
so it'll be like
myArray.filter(x => x.testKey === myTestKey)?.[0]
I'll like to throw some more light on what exactly happens with my above question case.
myArray.filter(x => x.testKey === myTestKey)?[0]
Transpiles to
const result = myArray.filter(x => x.testKey === myTestKey) ? [0] : ;
Due to which it throws the error since there's something missing after : and you probably don't want your code to be transpilled to this.
Thanks to Certain Performance's answer I learned new things about typescript especially the tool https://www.typescriptlang.org/play/index.html .
ECMA 262 (2020) which I am testing on Edge Chromium 84 can execute the Optional Chaining operator without TypeScript transpiler:
// All result are undefined
const a = {};
console.log(a?.b);
console.log(a?.["b-foo-1"]);
console.log(a?.b?.());
// Note that the following statements throw exceptions:
a?.(); // TypeError: a is not a function
a?.b(); // TypeError: a?.b is not a function
CanIUse: Chrome 80+, Firefox 74+
After a bit of searching the new page in the official documentation, it was discovered.
You need to put a . after the ? to use optional chaining.
So it will be so,
myArray.filter(x => x.testKey === myTestKey)?.[0]
Used only ? Makes the compiler think that you are trying to use a conditional operator (then it causes an error because it doesn't see a : later)
It's not necessary that the function is inside the object, you can run a function using optional chaining also like this:
someFunction?.();
If someFunction exists it will run, otherwise it will skip the execution and it will not error.
This technique actually is very useful especially if you work with reusable components and some components might not have this function.
Well, even though we figured out the correct syntax, the code doesn't make much sense to me.
The optional chaining in the code above is making sure, that the result of myArray.filter(x => x.testKey === myTestKey) is not null and not undefined (you can have a look at the TS output). But it is not possible anyway, because the result of the filter method is always an array. Since JavaScript doesn't throw "Array bounds exceeded", you are always safe when you try to access any index - you will get undefined if this element doesn't exist.
More example to make it clear:
const myArray: string[] = undefined
console.log(myArray.filter(x => x)?.[0]) //throws Cannot read property 'filter' of undefined
//in this example the optional chaining protects us from undefined array
const myArray: string[] = undefined
console.log(myArray?.filter(x => x)[0]) //outputs "undefined"

Why use _.get(err, ['data', 'error', 'code']) if you could simply do err.data.error.code?

So I've been looking for an answer given to me in a nodeJS test.
Why use _.get(err, ['data', 'error', 'code']) if you could simply do err.data.error.code?
And can you think of a better name for err here?
after 3 days of searching i can't figure out an answer ,I may need some help
Code:
if (_.includes(errorCodesOnWhichToRemoveSynchronization,
_.get(err, ['data', 'error', 'code']))) {
// ...
}
Update
A preferred way to do this without any external library is to leverage optional chaining.
err?.data?.error?.code
Lodash's _.get is failsafe. If any of the intermediate property doesn't exist, it simply returns undefined, instead of throwing an error.
See the demo below:
var obj = {
data: {
}
};
try {
console.log(obj.data.error.code);
} catch(e) {
console.log('regular method throws error');
}
console.log('lodash returns', _.get(obj, ['data', 'error', 'code']));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
By the way, in your case, ['data', 'error', 'code'] can simply be data.error.code.
Imagine err.data is undefined or an empty object. In that case, you would get an exception that js cannot read error from undefined. Using underscore/lodash's get function is a "safer" way to access it. It does not throw that exception and just returns undefined.
Optional Chaining (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
err?.data?.error?.code
Lodash has few methods to access properties that are quite handy and _.get is one of them where the main idea is for you to not have to worry if the path is not valid due to a falsy value along the way. Not only that but it has a defaultValue 3rd parameter (different in lodash/fp) where you could set a default value if the path asked for is invalid:
_.get(object, 'a.b.c', 'foo'); // if a.b.c is falsy you would get 'foo'
_.has is another one which would return a boolean if the requested path exists or not.
_.result is super handy when it comes to getting a value from a path which goes trough functions. Think about knockout and observables for example where to get from a to b you would usually need to do a().b().c().myValue ... so with _.result you would do:
_.result(obj, 'a.b.c.myValue')

What is the appropriate / recommended way to use hasOwnProperty?

Provided that the object MAY contain own property called "hasOwnProperty":
> a={abc: 123};
{ abc: 123 }
> a.hasOwnProperty("abc");
true
> a['hasOwnProperty'] = 1;
1
> a.hasOwnProperty("abc");
TypeError: a.hasOwnProperty is not a function
...
This works, kinda ugly interface, if you think about Object.keys(), Object.assign() ETC.. So, is there a better way?
> Object.hasOwnProperty.call(a, "abc");
true
> Object.hasOwnProperty.call(a, "hasOwnProperty");
true
And why shouldn't the solution be the only recommended way? Using methods directly from an object seems like a recipe for a failure, especially if it is containing external data (not in one's control)
The appropriate/recommended way to use hasOwnProperty is as a filter, or a means to determine whether an object... well, has that property. Just they way you are using it in your second command a.hasOwnProperty('abc').
By overwriting the Object hasOwnProperty property with a['hasOwnProperty'] = 1, while it's safe and valid, just removes the ability to use the hasOwnProperty function on that Object.
Am I missing your true question here? It seems like you already knew this from your example.
By
'using methods directly from an object seems like a recipe for a failure
are you referring to something like this:
> dog = {speak: function() {console.log('ruff! ruff!')}};
> dog.speak(); // ruff! ruff!
Because that is extremely useful in many ways as you can imagine.
If you can use ECMAScript 2015 you can try Reflect.getOwnPropertyDescriptor.
It returns a property descriptor of the given property if it exists on the object, undefined otherwise.
To simplify you can create this function:
var hasOwnProp = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== undefined;
var obj = new Object();
obj.prop = 'exists';
console.log('Using hasOwnProperty')
console.log('prop: ' + obj.hasOwnProperty('prop'));
console.log('toString: ' + obj.hasOwnProperty('toString'));
console.log('hasOwnProperty: ' + obj.hasOwnProperty('hasOwnProperty'));
var hasOwnProp = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== undefined;
console.log('Using getOwnPropertyDescriptor')
console.log('prop: ' + hasOwnProp(obj, 'prop'));
console.log('toString: ' + hasOwnProp(obj, 'toString'));
console.log('hasOwnProperty: ' + hasOwnProp(obj, 'hasOwnProperty'));
obj['hasOwnProperty'] = 1;
console.log('hasOwnProperty: ' + hasOwnProp(obj, 'hasOwnProperty'));
Any built-in can be overridden in JS - it's generally considered best practice to avoid overriding any native methods where possible. If the original functionality is preserved it's OK as it will still behave as expected and even could possibly extended further if overridden correctly again.
As that's considered best practice I recommend either remapping the keys to avoid overriding them. If remapping the keys is not an option then you can maybe make it feel a little less messy by either locally referencing/wrapping Object.hasOwnProperty or Object.prototype.hasOwnProperty. In the case of hasOwnProperty you could possibly implement an iterator (as iterating over enumerable non-inherited properties is a very common use of hasOwnProperty) method to reduce the likelihood of its use. There's always still the risk of someone less familiar with your object attempting to directly iterate so I really feel that key mapping is the safer bet even if it does cause a slight difference in between server-side keys and local ones.
A key mapping could be as simple as a suffix using hasOwnProperty_data instead of hasOwnProperty this would mean objects would behave as expected and your IDE's autocomplete likely will still be close enough to know what the property represents.
A mapping function might look like the following:
function remapKeys(myObj){
for(var key in myObj){
if(Object.prototype.hasOwnProperty.call(myObj, key)){
if((key in Object) && Object[key] !== myObj[key]){ // Check key is present on Object and that it's different ie an overridden property
myObj[key + "_data"] = myObj[key];
delete myObj[key]; // Remove the key
}
}
}
return myObj; // Alters the object directly so no need to return but safer
}
// Test
var a = {};
a.hasOwnProperty = function(){ return 'overridden'; };
a.otherProp = 'test';
remapKeys(a);
console.log(a); // a { hasOwnProperty_data : function(){ return 'overridden';}, otherProp: 'test' }
console.log(a.hasOwnProperty('otherProp')); // true

Replace 'undefined' with a custom exception

Say I have some object
> var a = {'a': 1, 'b': 2}
undefined
> a.c
undefined
I would like it if this "undefined" attribute c were to instead raise some sort of custom error.
Is there some way to over-ride my object's default behavior, or perhaps invoke it in some way as to provide a callback in case the lookup returns undefined?
I found this question from a couple of years ago that appears related.
I ask because I feel generic TypeError: cannot call method 'foo' of undefined errors might be better replaced with more meaningful feedback.
You'll be testing for an error one way or another. Overriding it doesn't make a lot of sense, especially since you'll just make your code base more complicated and undefined actually makes sense here. The property c is literally undefined and so this is quite readable (especially to new people working with your code):
if (a.c) { //or could be a.c !== undefined,
//but if(a.c) will catch other falsey values
a.c.something();
}
You can use try, throw and catch() for this is exact situation.
Here's an example:
var a = {'a': 1,'b': 2};
try {
if (a.c === undefined) throw "Sorry you need to create the property 'a.c first";
} catch (err) {
console.log(err);
}
In JS Fiddle
Whatever you put as the string after throw becomes the argument passed to catch(err). So, instead of it saying undefined it says Sorry you need to create the property 'a.c first
If you want to get a more meaningful feedback, you can use Firefox.
At least on its version 26, I get
TypeError: a.c is undefined
when doing
var a = {};
a.c.foo(); // TypeError: a.c is undefined
There's a js library called underscore.js with methods like _.isUndefined(), _.isNull(), _.isEmpty() or _.isNan() that may be useful for you in some case.
But the way javascript capture exceptions is like Deryck said. Anyway I'd prefer have more control throw if validations with the underscore methods when I need it.

JavaScript: detect if argument is array instead of object (Node.JS)

How should I detect if the argument is an array because typeof [] returns 'object' and I want to distinguish between arrays and objects.
It is possible that object will look like {"0":"string","1":"string","length":"2"} but I don't want it to come out as an array if it is in fact an object looking like an array.
JSON.parse and JSON.stringify are able to make this distinction. How can I do it?
I am using Node.JS which is based on V8 the same as Chrome.
Array.isArray
native V8 function. It's fast, it's always correct. This is part of ES5.
arr instanceof Array
Checks whether the object was made with the array constructor.
_.isArray // underscore method.
A method from underscore. Here is a snippet taken from the their source
var toString = Object.prototype.toString,
nativeIsArray = Array.isArray;
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
This method takes an object and calls the Object.prototype.toString method on it. This will always return [object Array] for arrays.
In my personal experience I find asking the toString method is the most effective but it's not as short or readable as instanceof Array nor is it as fast as Array.isArray but that's ES5 code and I tend to avoid using it for portability.
I would personally recommend you try using underscore, which is a library with common utility methods in it. It has a lot of useful functions that DRY up your code.
Try this code:
Array.isArray(argument)
How about:
your_object instanceof Array
In V8 in Chrome I get
[] instanceof Array
> true
({}) instanceof Array
> false
({"0":"string","1":"string","length":"2"}) instanceof Array
> false
I looks like this question has several good answers, but for completeness I would add another option, which have not been suggested earlier.
In order to check if something is an array, you can use Node.js util native module and its isArray() function.
Example:
var util = require('util');
util.isArray([]); // true
util.isArray(new Array); // true
util.isArray({"0":"string","1":"string","length":"2"}); // false
With that method you do not have to worry about JS standards implemented by V8 as it will always show the right answer.
Try this way:
console.log(Object.prototype.toString.call(arg).replace(/^[object (.+)]$/, '$1').toLowerCase())

Categories

Resources