Say I want to access a.b.c.d and I'm not sure if b or c exist.
The 'naive' check would be:
if (a.b && a.b.c && a.b.c.d == 5) doSomething(a.b.c.d);
I thought this over and wrote this function that improves this:
Object.prototype.parse = function (keys, def) {
return keys.split('.').reduce(function (prev, curr) {
if (prev) {
return prev[curr];
}
}, this) || def;
};
And you would use it like this:
var a = {
b: {
c: {
d: 5
}
}
};
console.log(a.parse('b.c.d', 3)); // If b, c or d are undefined return 3
But I'm wondering if I'm missing a better, native way to achieve this instead of having to add this function to projects.
Thanks!
Maybe not quite what you were asking for, but probably as close to 'native' as you can get (a slightly more compact version of split/reduce snippet you provided):
var a = {b:{c:{d:5}}};
("b.c.d").split(".").reduce(function(p,c){return p && p[c];},a); //5
("b.c.e").split(".").reduce(function(p,c){return p && p[c];},a); //undefined
If you were hoping for a solution with a string like "a.b.c.d", then you'll need to use eval (not recoomended) or the object a will need to be global (also not recommended) or the object will need to be a property of another object which is little self-defeating.
The only native way is eval, but I wouldn't recommend it as can be used to execute arbitrary code. This might be OK, but not if your "a.b.c.d" style strings come from untrusted users. I'd stick with your handcrafted solution or use dotty.
var a = {
b: {
c: {
d: 5
}
}
};
console.log(eval("a.b.c.d"));
eval() will throw the same error that the native equivalent would when b or c are not defined so you'll need to wrap with a try {} catch {} block.
I think I have another solution to this problem, I was trying to achieve this for minutes. If you are working on window scope you can use my function to see if the object exist or return a default value.
function parseObj(obj){
try{
return eval(obj);
}catch(e){
return null;
}
}
Usage
alert(parseObj("a.b.c.d"));
I don't know if this is what you looking for but I am sure it will give you an another idea. Have a nice work.
Related
In this console:
When B is set to A but A was destroyed afterwards, B.b() throws an error because A is truly undefined.
How can one avoid this?
PS: I am aware that I can simply return this (or change the function in some way) but that doesn't fulfill my purposes.
EDIT: How do I 'localise' and somehow tell javascript that by A, I mean B even if A is undefined, WITHOUT ALTERING THE FUNCTION ITSELF?
You should change your A to look like the following:
let A = {
a: "a",
b: function() {
// change A.a to this.a
return this.a;
}
};
Right now since it references A when you destroy A Even though B is already equal to A the b method still needs the original A to exist. Swapping it for this will fix the issue.
The way the question and example is written that "error" cannot be avoided. return A.a; will and SHOULD be undefined based on your example because you are assigning A = undefined;
Also, if you are trying to clone an object, there are a couple of other options you should look into like:
let B = JSON.parse(JSON.stringify(A));
let B = Object.assign({}, A); MDN
Javascript classes
And maybe check out this answer: How do I correctly clone a JavaScript object?
To the new viewers, if you want to do this without altering the function:
var A = (function() {
var A = {a: 90, b: function() { return A.a }}; // same object as in the Q
return A;
})()
console.log(A.b()) // 90
var B = A;
A = undefined;
console.log(B.b()) // Also 90
B.a = 89;
console.log(B.b()) // 89
This question already has answers here:
if (key in object) or if(object.hasOwnProperty(key)
(9 answers)
Closed 4 years ago.
I was wondering if there was a standard library or external library (eg lodash) function that essentially does following:
function f(options) {
const x = options.x;
if (x === null || x === undefined) throw new Error('x is required option');
...
}
I have searched various key words in the lodash documentation but have yet to find something like this.
To check if a property exists, you can just write if(x in options)... Libraries exist to make tedious or difficult operations simple, This is such a simple operation that no library is needed and you'd be unlikely to find it added to a library.
While .hasOwnProperty() can work, it will only test for properties attached directly to the object in quesiton, while the in approach tests for inherited properties as well.
But, be careful about throwing errors. It's not generally something that you want to do for performance reasons. It's better to return a code or message.
var options = {
foo:42,
bar:"doesn't matter"
};
function propTest(propName, obj){
// Instead of throwing an error, return a value that indicates success or not
return (propName in obj) ? true : false;
}
console.log(propTest("foo", options));
console.log(propTest("bar", options));
console.log(propTest("baz", options));
You can write a function that iterates through an objects keys and looks for undefined ones
function CheckObjectProperties(obj)
{
for (var key in obj)
{
if(!obj[key])
{
throw new Error('Object has one or more undefined members');
}
}
}
or if you wanna check if an unknown object you can do something along the line of this. This approach is much more dynamic and reusable :D
function CheckObjectForProperties(obj, arraryOfStringProperties)
{
for(let i = 0; i < arraryOfStringProperties.length; i++)
{
let key = arraryOfStringProperties[i];
if(!obj[key])
{
throw new Error('Member not Found in Given Object.');
}
}
}
let myObject = {
x : 1,
y : 2,
z : undefined
}
let requiredProperties = ['x', 'y', 'z'];
CheckObjectForProperties(myObject, requiredProperties);
I've found this string in JavaScript code.
var c = (a.b !== null) ? a.b : null;
This is a shorthand of an if-else statement, however the value null is assigned if it is null. Isn't that ALWAYS equivalent to
var c = a.b
including all cases - exceptions, null, undefined, etc?
In another words, are these lines (always) equivalent?
var c = (a.b !== null) ? a.b : null;
-vs-
var c = a.b
No, they AREN'T NECESSARILY EQUAL always if b is a getter that updates a variable. It's bad practice to code this way though
var log = 0;
var a = {
get b() {
log++;
return log;
}
}
var c = (a.b !== null) ? a.b : null;
// outputs 2
console.log(c);
var log = 0;
var a = {
get b() {
log++;
return log;
}
}
var c = a.b;
// outputs 1
console.log(c);
These statements are logically equivalent.
That being said, and as mentioned in another answer, if a.b has side effects, the statements will not result in the same program state.
This could be readily obvious in the form of var c having a different value depending on which of these statements are executed, or more hidden, if a.b modifies something elsewhere in the program.
Refactoring
As refactoring has been discussed, I'll touch on it briefly. As the above has hopefully made obvious, directly refactoring would not be safe in all scenarios. However, I would still recommend a refactor of one kind or another.
The two possible situations as I see them are:
a.b has no side effects, direct refactoring is safe
a.b has hidden side effects. This represents very unclear, confusing,
and just downright bad code. It should be refactored so that all
changes happening during the statement are clear and obvious to a
reader (hopefully intuitively so, as well as supported by comments).
As #potatopeelings already pointed out, the two possible statements are not always equivalent, since one can write obscure code, which will have different results.
However, if I see a code, like
var c = (a.b !== null) ? a.b : null;
I will assume that the code's intention is
var c = a.b;
so I will change it to make the code prettier. If I will be negatively surprised, that is, the code does not pass the testing phases due to this change, then I will try to find the author of a.b with git blame.
So, my answer is, that the two statements are not equivalent, but should be equivalent in well-written code.
Well, actually not even
var c = (a !== null) ? a : null;
is guaranteed to be equivalent to
var c = a;
when a is resolved by a getter or an ES6 proxy handler on the global object.
Hence for instance, this assign to c the value 0:
Object.defineProperty(self, 'a', { get: function() { return c ^= 1; } });
var c = (a !== null) ? a : null;
console.log(c);
while this assigns to c the value 1:
Object.defineProperty(self, 'a', { get: function() { return c ^= 1; } });
var c = a;
console.log(c);
You're right, var c = a.b is exactly the same as var c = (a.b !== null) ? a.b : null;
My guess is the null in the ternary operator was meant to be anything except null, a default value if you will.
The reason for that confusingly odd syntax is because a.b might be an empty string OR undefined, and apparently an empty string is valid input.
Also, note: a.b might be a function.
This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 6 years ago.
This is something that I come up against quite often in Javascript. Let's say I have an object like this:
var acquaintances = {
types: {
friends: {
billy: 6,
jascinta: 44,
john: 91
others: ["Matt", "Phil", "Jenny", "Anna"]
},
coworkers: {
matt: 1
}
}
}
In my theoretical program, all I know for sure is that acquaintances is an object; I have no idea whether acquaintances.types has been set, or whether friends has been set within it.
How can I efficiently check whether acquaintances.types.friends.others exists?
What I would normally do is:
if(acquaintances.types){
if(aquaintances.types.friends){
if(acquaintances.types.friends.others){
// do stuff with the "others" array here
}
}
}
Aside from being laborious, these nested if statements are a bit of a nightmare to manage (in practice my objects have far more levels than this!). But if I were to just try something like if(acquaintances.types.friends.others){) straight off the bat, and types hasn't been set yet, then the program will crash.
What ways does Javascript have of doing this in a neat, manageable way?
An alternative approach is:
((acquaintances.types || {}).friends || {}).others
which is shorter than other solutions, but may or may not thrill you.
You can also build a little helper to make the same idea a tiny bit more palatable:
function maybe(o) { return o || {}; }
Now you can do
maybe(maybe(acquaintances.types).friends).others
If you don't mind writing property names as strings, you could make a little helper:
function maybe(obj) {
return Object.defineProperty(
obj || {},
'get',
{ value: function(prop) { return maybe(obj[prop]); }
);
}
Now you can write
maybe(acquaintances.types').get('friends').others
In ES6, you can do this, albeit clumsily, using destructuring assignment with defaults:
var { types: { friends: { others } = {} } = {} } = acquaintances;
If you want to use this in an expression context, instead of assigning to a variable, in theory you could use argument destructuring:
(({ types: { friends: { others } = {} } = {} }) => others)(acquaintances)
After all is said and done, the standard approach remains
acquaintances.types &&
acquaintances.types.friends &&
acquaintances.types.friends.others
This is why there is an active (?) discussion in the ES6 design groups about a CoffeeScript-like existential operator, but it does not seem to be converging very rapidly.
The and operator is sequential so you can do this without nesting if statements.
if(acquaintances.types && aquaintances.types.friends && acquaintances.types.friends.others){
//acquaintances.types.friends.others exists!
}
It's not nice in JavaScript.
You could add them to one big condition...
if (obj.prop && obj.prop.someOtherProp) { }
...or write a helper function where you pass an object and a string...
var isPropSet = function(object, propPath) {
return !! propPath.split('.')
.reduce(function(object, prop) { return object[prop] || {}; }, object);
};
isPropSet(obj, 'prop.someOtherProp);
...or you could use CoffeeScript and its ? operator...
obj.prop?.someOtherProp
You could also wrap the lookup in a try/catch, but I wouldn't recommend it.
Instead of this:
if(acquaintances.types){
if(aquaintances.types.friends){
if(acquaintances.types.friends.others){
// do stuff with the "others" array here
}
}
}
Try this:
if(acquaintances &&
acquaintances.types &&
acquaintances.types.friends &&
acquaintances.types.friends.others) {
}
Or
acquaintances &&
acquaintances.types &&
acquaintances.types.friends &&
acquaintances.types.friends.others ?
doSomething() : doSomethingElse()
When accessing nested objects using dot notation, I always have to make sure that the previous object exists, which gets pretty exhausting.
I basically want to avoid long if chains like
if (a && a.b && a.b.c && a.b.c[0] ... ) { v = a.b.c[0]; }
The only other thing I can think of is via the use of a try catch.
var v; try { v = a.b.c[0].d.e; } catch (e) {}
Is there a better pattern for this?
I think you've got the two prettiest solutions already.
But note that for something like, say, obj.obj.string.length your first solution will fail if string === "". Since an empty string is falsey, it'll trip the && guard.
But speaking of strings, you could do something like:
function getNestedProperty(obj, propChain) {
var props = propChain.slice(0), prop = props.shift();
if(typeof obj[prop] !== "undefined") {
if(props.length) {
return getNestedProperty(obj[prop], props);
} else {
return obj[prop];
}
}
}
var v = getNestedProperty(a, ["b", "c", 0, "d", "e"]);
Yeah... not too pretty :P
I'd say that, of the solutions proposed, try...catch is probably the simplest way to go
How about this one:
var hasProperty = function (object, property) {
var properties = property.split('.'),
temp = object;
while (temp && properties.length) {
temp = temp[properties.shift()];
}
return !!temp;
};
and then use it like:
if (a && hasProperty(a, 'b.c.0' ) { v = a.b.c[0]; }
The scenario you are referring to in your question is also called "optional chaining". Some languages already support it by now – for example C# has so called null-conditional operators which allow you to short-circuit your expressions:
var count = customers?[0]?.Orders?.Count();
Unfortunately, this feature has not yet made it into the current JS specifications.
There is an open Stage 1 proposol for "optional chaining" that can be tracked here.
This would allow you to write...
a?.b[3].c?.(x).d
...instead of:
a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
If you want to take the risk and use it already at this early stage, you can target it via babel to include it in your project.
It's rather evil, but this should work and doesn't look too horrible:
var i = !a ? null : !a.b ? null : !a.b.c ? null : !a.b.c.d ? a.b.c.d.e;
The reason for the ! is to invert the test flag, to allow the success case to be the last expression in the ?:. That allows us to chain them together like this.
Do check the operator precedence if you want to do this for real (I did some very basic tests and I think I got it right). And do expect people to point and laugh if they see it in your code.