Null Checks for a large hierarchical object in Javascript [duplicate] - javascript

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 8 years ago.
We have a large hierarchical object (worst possible legacy design) in javascript. The problem I am facing is that there is a list of null checks I need to perform whenever I want to access an element deep within the object structure.
Say I have a bank object which contains a list of customers and I want to get the address of the first customer,
if(bank != null ||
bank.customerlist != null ||
bank.customerlist.customer[0] != null ||
bank.customerlist.customer[0].address != null )
{
transactionAddress = bank.customerlist.customer[0].address;
}
This is just a small example,I cannot believe so many null checks are required
just to access a single value.
It there a better way around this?

You can use try catch block:
try {
var transactionAddress = bank.customerlist.customer[0].address;
} catch(e) {
// handle the error here
}

You could create your own accesser function:
function access(obj, path) {
var arr = path.split('/');
while(obj && arr.length)
obj = obj[arr.shift()];
return obj;
}
And use it like this:
var bank = {
customerlist: {customer: [{address: 'foo'}]}
}
access(bank, 'customerlist/customer/0/address'); // 'foo'
access(bank, 'bar/foo/foobar'); // undefined (no error)
Also consider using...
function access(obj, path) {
var arr = path.split('/');
while(obj!=null && arr.length)
obj = obj[arr.shift()];
return obj;
}
...if you want to use access with non-objects, e.g. you want access('', 'length') to return 0
Explaining,
function access(obj, path) {
var arr = path.split('/');
while (
obj /* To avoid `null` and `undefined`. Also consider `obj != null` */
&& /* Logical AND */
arr.length /* Check that `arr` still has elements */
) {
obj = obj[arr.shift()]; /* `arr.shift()` extracts the first
element is `arr` */
}
return obj;
}

Related

How to replace a dynamically specified attribute in JS object

The attribute(or the nested object) is selected dynamically based on conditions. It can be one of the 4 possibilities as follows:
var tempData = o.title ? o["properties"] || o["items"]["properties"] : o[k]["properties"] || o[k]["items"]["properties"];
Then I get this new data, I want to replace the above selected with.
var newData = //some new Object
I want to replace whatever above selected with the new data. I could do the following (go through the condition again and set the new data):
if(o.title){
if (o["properties"]) {
o["properties"] = newData;
} else if (o["items"]["properties"]) {
o["items"]["properties"] = newData;
}
}else{
if (o[k]["properties"]) {
o[k]["properties"] = newData;
} else if (o[k]["items"]["properties"]) {
o[k]["items"]["properties"] = newData;
}
}
But it doesn't look good. What is the more sophisticated way of achieving this?
It is unclear if you are generically attempting to replace any properties property with the newData, or if you are wanting it to specifically be one of the ones you have specified in your code. I have assumed that you are only wanting to replace the ones you specifically have shown in your code.
Note: The following assumes that it is not possible for the value of the properties property to evaluate to false. If it is possible for it to have a value that evaluates to false, this will fail.
As a first pass, I would do something like:
var p;
if (o.title) {
p=o;
} else {
p=o[k];
}
if (p.properties) {
p.properties = newData;
} else if (p.items.properties) {
p.items.properties = newData;
}
However, that relies on:
o is not null or undefined.
o.title does not evaluate to false, if you are trying to test for the existence of o.title.
k is valid/defined.
p (i.e. o[k]) is not null or undefined (i.e. is an Object)
p.properties does not evaluate to false, if you are testing for existence
p.items is not null or undefined (i.e. is an Object)
p.items.properties does not evaluate to false, if you are testing for existence
A more robust implementation would be:
if (typeof o === 'object' && o !== null) {
var p;
if (o.hasOwnProperty('title')) {
p = o;
} else {
p = o[k];
}
if (typeof p === 'object' && p !== null) {
if (p.hasOwnProperty('properties')) {
p.properties = newData;
} else if (typeof p.items === 'object' && p.items !== null
&& p.items.hasOwnProperty('properties')) {
p.items.properties = newData;
}
}
}
This still relies on:
k is valid/defined.
Basically, it is OK to use shortcuts like if(o.title) to test for existence, if you know that
the possible values for o can not include ones which might make your code throw an error (e.g o is null or undefined), and
the possible values for o.title do not evaluate to false when the property actually exists (e.g. o.title is null, undefined (yes, the property can exist, but have the value undefined), false, 0, '', etc.).
If you are going to perform the replacements in other areas of your code, or if you are going to use property keys other than hard coded items, and properties, then you should create a function. Assuming you are only performing this replacement in this section of your code, using a variable to hold the object in which you are looking for properties is faster/more efficient than creating a function.
Ok, from what i can understand here, it's like you are trying to replace the "properties" with the new data, and you want this to be able to be done dynamically, or maybe i can say, you need to do this regardless the structure.
lets see, if your objective is anything that end up with "properties", lets do it like this:
function recReplace(current,target,replacement){
for (var i in current){
if (i == target){
current[i] = replacement;
}
else{
recReplace(current[i],target,replacement);
}
}
}
And in the end you call
recReplace(o,"properties",newData);
But this will replace whole "properties" key with newData in DFS way, you can do additional conditional if you want to replace it only the first occurence

Dependent Object Type Check Javascript Seems Redundant

Is there a better way of doing this?
If I do this:
if(typeof someObject[bar][foo] == "function"){
// Do something
}
I get a someObject[bar][foo] is not an object error which it's referring to someObject[bar] if bar does not exist. This means that the code assumes you know that someObj[bar] is defined. So my solution was was this:
if(typeof someObj[bar] === "object"){
if(typeof someObject[bar][foo] == "function"){
// Do something
}
}
If we want to make an effort to reduce code lines and to make good, clean, readable code then looks redundant and ugly. Is there a better way of doing this without having to go through two if points? I realize that it's not that big of a deal, but I'm wondering if there is a better way.
I'm afraid there's no simple explanation.
In which circumstance/situation do you expect an object to behave conditionally? To give you an example of what I use ... to go advanced ... and in an attempt to go as simple as possible at the same time ...
Are you looping through a whole set of array like objects?
Can you trust the object so you already know what to expect?
object comparison for different data types?
.[I need some editing :s]
/**
* #description Get an object from a list of objects by searching for a key:value pair
* #param {Object} obj : -literal, json
* #param {String} val : the value you seek
* #param {String} key : the key
* #param {Boolean} isTypeComparison : if set to true, the key and value will be checked against it's type as well
*/
getObjectProperty: function (obj, val, key, isTypeComparison) {
var property, o;
for (property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] === 'object') {
o = this.getObjectProperty(obj[property], val, key);
if (o) {
break;
}
} else {
// found a property which is not an object
if (isTypeComparison) {
if (property === key && obj[property] === val) {
// we got a match
o = obj;
break;
}
} else {
if (property == key && obj[property] == val) {
// we got a match
o = obj;
break;
}
}
}
}
}
return o || undefined;
},
To add some sort of value to your question, in all these loops above you see a struggle to an expectation. I've used this code to search through an ajax contact list, attached to a list. So you definitely need to write more code to meet depth and trust requirements.
If you generalize your problem, you're basically asking if you can verify a sort of 'path' in an object as being legitimate. The way I would do this is with an a function that takes the object and the desired 'path':
function has(obj, path){
var temp = obj;
var path = path.split('.');
for(var i = 0; i < path.length; i++){
if(temp[path[i]])
temp = temp[path[i]];//path still exists
else
return false;//path ends here
}
return true;//the whole path could be followed
}
This example uses a path passed as 'bar.foo' but you could easily adjust for an array ['bar','foo'] or so that it is a variable amount of arguments passed in.
This would make your example:
if(has(someObject, bar + '.' + foo)){
if(typeof someObject[bar][foo] == "function"){
// Do something
}
}
While this doesn't reduce this example in specific, if you had a much longer path to search, this could significantly reduce if statements chained together.
You could modify the function so that it returns the value specified by the path should it exist instead of true so that you only deal with one line:
function get(obj, path){
var temp = obj;
var path = path.split('.');
for(var i = 0; i < path.length; i++){
if(temp[path[i]] !== undefined)
temp = temp[path[i]];//path still exists
else
return undefined;//path ends here
}
return temp;//the whole path could be followed
}
if(typeof get(someObject, bar + '.' + foo) === 'function'){
//do something
}
if( someObject.bar && someObject.bar.foo && typeof someObject.bar.foo === "function" ){
...
}
or the same but with the better visibility notation style:
if( someObject.bar
&& someObject.bar.foo
&& typeof someObject.bar.foo === "function" ){
...
}

For...in loop filtering only objects

Is there a way to filter out everything inside of a for...in loop to only get the objects?
I'm writing a function to loop through nested objects to find certain pieces of data and then save it to localStorage.
Example:
var equipped = {
data: [the rest of the properties of equipped go here],
tool: {
data: [the rest of the properties of tool go here],
axe: {
data: [the rest of the properties of axe go here],
iron: {...},
steel: {...}
}
}
}
The tool/axe/metal properties are all generated dynamically and is different each time. Inside the metal properties is the data I'm trying to save. I would normally just loop through the array if I was trying to access the data (Using knockoutjs for binding, it's much easier to just foreach the data array), but I'm using the variable from a for...in loop to build the rest of the tree in my localStorage object before stringifying it.
How I'm reading the object:
for (var type in equipped) {
if (check goes here) {
savedValue.equipped[type] = {};
for (var category in equipped[type]) {
etc etc...
}
}
}
I understand that everything is an object type so I can't just do an instanceof or typeof on a defined object to filter them out. Is there another easy way to do it inside of an if statement or do I have to make each step of the tree from a constructor so I can instanceof RealObject?
Either of these should do well:
function isObject(val) {
if (val === null) { return false;}
return (typeof val === 'object');
}
or
function isObject(obj) {
return obj === Object(obj);
}
or
// this only works with object literals
function isObject(val) {
return (!!val) && (val.constructor === Object);
};
this last one, gives me the following:
console.log(isObject()); // false
console.log(isObject([])); // false
console.log(isObject(new Date)); // false
console.log(isObject({})); // true
console.log(isObject(null)); // false
console.log(isObject(true)); // false
console.log(isObject(1)); // false
console.log(isObject('someValueString')); // false
so, something like:
for (var type in equipped) {
if (isObject(type)) {
savedValue.equipped[type] = {};
for (var category in equipped[type]) {
etc etc...
}
}
}
Note: You can also try the following, but I have not used it. So you'd have to go thru your use cases.
Object.getPrototypeOf
Here is the code to check whether the variable is object or not:
function isJsonObject( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || obj.toString() !== "[object Object]" || obj.nodeType || obj.setInterval ) {
return false;
}
// Not own constructor property must be Object
if ( obj.constructor
&& !obj.hasOwnProperty("constructor")
&& !obj.constructor.prototype.hasOwnProperty("isPrototypeOf")) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for ( key in obj ) {}
return key === undefined || obj.hasOwnProperty( key );
}
There's an old hack for type detection I've used previously.
var classChecker = {}.toString;
classChecker.call({});
classChecker.call(function() {});
classChecker.call([]);
// etc...
// More immediately relevant use:
var savedValue = {
equipped: {}
};
var objectString = classChecker.call({});
for (var type in equipped) {
if (classChecker.call(equipped[type]) === objectString) {
savedValue.equipped[type] = {};
for (var category in equipped[type]) {
// ...
}
}
}
console.log(savedValue);
See http://plnkr.co/edit/nKLQsOdcurrpUCg7cOoJ?p=preview for a working sample. (open your console to view output)

JavaScript check if property defined [duplicate]

This question already has answers here:
What's the simplest approach to check existence of deeply-nested object property in JavaScript? [duplicate]
(7 answers)
Closed 10 years ago.
What is the recommended way to check if an object property like obj.prop.otherprop.another is defined?
if(obj && obj.prop && obj.prop.otherprop && obj.prop.otherprop.another)
this works well, but enough ugly.
The most efficient way to do it is by checking for obj.prop.otherprop.another in a try{} catch(exception){} block. That would be the fastest if all the remaining exist; else the exception would be handled.
var a = null;
try {
a = obj.prop.otherprop.another;
} catch(e) {
obj = obj || {};
obj.prop = obj.prop || {};
obj.prop.otherprop = obj.prop.otherprop || {};
obj.prop.otherprop.another = {};
a = obj.prop.otherprop.another ;
}
Not saying this is better but ...
x = null
try {
x = obj.prop.otherprop.another;
} catch() {}
// ...
Or alternatively ...
function resolve(obj, path) {
path = path.split('.');
while (path.length && obj) obj = obj[path.shift()];
return obj;
}
x = resolve(obj, 'prop.otherprop.another');
... but I guess the actual answer is that there isn't a best-practice for this. Not that I'm aware of.
If you're in a silly mood, this would work:
if ((((obj || {}).prop || {}).anotherprop || {}).another) { ... }
But I don't know if initializing three new objects is really worth not having to repeatedly type out the path.

Pluck specific javascript value from an object based on an array of indexes

Given a nested object like this:
var cars = {
"bentley": {
"suppliers": [
{
"location": "England",
"name": "Sheffield Mines"}
]
// ...
}
};
and an array like this ["bentley", "suppliers", "0", "name"], is there an existing function that will pluck the deepest element, i.e. pluck_innards(cars, ['bentley', "suppliers", "0", "name"]) and that returns "Sheffield Mines".
In other words, is there a function (which I will name deep_pluck) where
deep_pluck(cars, ['bentley', 'suppliers', '0', 'name'])
=== cars['bentley']['suppliers']['0']['name']
It seems to me that this is simple, yet common enough, to have probably been done in one of the Javascript utility libraries such as jQuery or lo-dash/underscore - but I have not seen it.
My thought is something trivial, along the lines of:
function deep_pluck(array, identities) {
var this_id = identities.shift();
if (identities.length > 0) {
return deep_pluck(array[this_id], identities);
}
return array[this_id];
}
Which I have posted on jsFiddle.
It would be helpful of course if the function were smart enough to identify when numerical indexes in arrays are needed. I am not sure offhand what other caveats may be a concern.
This is all a fairly long question for something I imagine has already been cleverly solved, but I thought to post this as I would interested in seeing what solutions are out there.
I don't think you'll have problems with Array indexes if you pass them as number 0.
Here's alternative version of your function without recursion:
function deep_pluck(object, identities) {
var result = object;
for(var i = 0; i < identities.length; i++) {
result = result[identities[i]];
}
return result;
}
Working example here: http://jsfiddle.net/AmH2w/1/
dotty.get(obj, pathspec) does it, accepting either an array or a dotted string as the pathspec.
Dotty is open source, and also has an exists method, and a putter.
The methodology is recursion and very similar to your idea, except that dotty includes a test for null/undefined objects so that it doesn't throw exceptions for trying to access an element of something that doesn't exist.
The dotty.get() source from the docs is posted below:
var get = module.exports.get = function get(object, path) {
if (typeof path === "string") {
path = path.split(".");
}
if (!(path instanceof Array) || path.length === 0) {
return;
}
path = path.slice();
var key = path.shift();
if (typeof object !== "object" || object === null) {
return;
}
if (path.length === 0) {
return object[key];
}
if (path.length) {
return get(object[key], path);
}
};
Although not a generic library, it seems that CasperJS has something of this kind with its utils.getPropertyPath function.
/**
* Retrieves the value of an Object foreign property using a dot-separated
* path string.
*
* Beware, this function doesn't handle object key names containing a dot.
*
* #param Object obj The source object
* #param String path Dot separated path, eg. "x.y.z"
*/
function getPropertyPath(obj, path) {
if (!isObject(obj) || !isString(path)) {
return undefined;
}
var value = obj;
path.split('.').forEach(function(property) {
if (typeof value === "object" && property in value) {
value = value[property];
} else {
value = undefined;
}
});
return value;
}
Edit:
I have come across implementations to solve this a couple times since, including:
the getObject plugin by Ben Alman (on Github).
one I rolled - see gist
Edit (2014)
I would also note the relatively new lodash.deep.
Here's a short ES6 implementation using reduce:
function get(obj, keyPath) {
return keyPath
.split(".")
.reduce((prev, curr) => prev[curr], obj);
}
Usage:
get(cars, "bentley.suppliers.0.name") // -> "Sheffield Mines"

Categories