Let's say I have an object:
var foo = {'bar1': {'baz1':1}};
And I try to access foo['bar2']['baz2']. If I was just trying to access foo['bar2'], JS would return undefined. But because I'm trying to get the next property after the undefined bar2, JS throws TypeError: Cannot read property 'baz2' of undefined.
Is there some automatic accessor for an object that first checks if baz2 exists in foo before trying to call its properties? I know I could use a try/catch block or an if statement, but I was hoping for a function along the lines of C++'s array::at, that would check for me.
You could write your own pretty easily:
function at(obj, property) {
var props = property.split('.');
for(var i = 0; i < props.length; i++) {
if (typeof obj === 'undefined') {
return;
}
obj = obj[props[i]];
}
return obj;
}
var foo = {'bar1': {'baz1':1}};
console.log(at(foo, 'bar1.baz1'));
// => 1
console.log(at(foo, 'bar2.baz2'));
// => undefined
Keep in mind this is ONLY for objects, like the one you have shown, and NOT for arrays (ie [0, 1, 2]) but my favorite is this one:
if ('bar1' in foo)
This is even useful for, say, HTML5 feature detection.
if ('localStorage' in window)
I could give you one for arrays too, but I feel like the more logical thing would be to compare its length to a given index. And...not insert undefined values into arrays. Y'know. =p
You can use the in operator:
if("bar2" in foo) {
//do stuff with foo['bar2']
}
or you can check to see if foo['bar2'] is undefined:
if(typeof foo['bar2'] !== "undefined") {
//do stuff with foo['bar2']
}
Also, what you're working with are objects and not arrays (well, they're associative arrays, but also objects in JavaScript).
foo['bar2'] && foo['bar2']['baz2'] will do the trick as well.
Related
I’m checking if a property 'lessons' (not inherited) is not present in obj,
All these give me true
(typeof obj['lessons'] == undefined)
(!(obj.hasOwnProperty('lessons')))
(!(hasOwnProperty.call(obj,'lessons')))
(!(_.has(obj, 'lessons')))
(!Object.prototype.hasOwnProperty.call(obj, 'lessons’))
but the property is present in the object, when i print keys by using (key in obj). I don't want to use it as it's very slow and my object is huge.
I found this on stackoverflow, but I don't understand what it's trying to do or how to use it with node.js.
I'd also like to know how are the mentioned ways of using hasOwnProperty are different.
EDIT
adding code
My code is:
console.log("LS:",JSON.stringify(obj)) ;
if (typeof obj['lessons'] == undefined)
console.log('typeof undefined');
else {console.log('typeof not undefined');}
if (!(obj.hasOwnProperty('lessons')))
console.log('hasOwnProperty: false');
else {console.log('hasOwnProperty not undefined');}
if (!(hasOwnProperty.call(obj,'lessons')))
console.log('hasOwnProperty.call');
else {console.log('hasOwnProperty.call not undefined');}
if (!(_.has(obj, 'lessons')))
console.log('_.hash');
else {console.log('_has not undefined');}
if (!(_.has(obj, 'lessons')))
{obj['lessons'] = {"levels": []};}
else
{console.log("has lesson level ");}
console.log("Lesson ", JSON.stringify(obj.lessons));
And the output I'm getting is:
LS: {"_id":"N9zmznzAWx","time":"1970-01-01T00:33:36.000Z","lessons":{"levels":["abc"]}}
typeof not undefined
hasOwnProperty: false
hasOwnProperty.call
_.hash
Lesson {"levels":[]}
Same case with all others..
SOLUTION
It works when I use JSON.parse(JSON.stringify(obj)) instead of obj.
Your checks aren't working because Mongoose document objects don't use simple object properties to expose their fields.
Instead, you can use the Document#get method to do this (with your obj renamed to doc):
var isMissing = (doc.get('lessons') === undefined);
Or you can create a plain JS object from your document by calling toObject() on it, and then use hasOwnProperty on that:
var obj = doc.toObject();
var isMissing = !obj.hasOwnProperty('lessons');
Here is a function that I wrote as an example to test three ways you have listed in your question:
function check(obj) {
if (!obj.hasOwnProperty("lessons")) {
console.log('nope');
}
if (!_.has(obj, 'lessons')) {
console.log('nope');
}
if (!obj.lessons) {
console.log('nope');
}
}
Here is JSBIN that runs the function on two objects, one with 'lessons' and one without:
Example JsBIN
typeof returns a string
var obj = {};
console.log(typeof (typeof obj.lessons));
https://jsfiddle.net/0ar2ku7v/
So you must compare it as such:
if (typeof obj.lessons === 'undefined')
console.log('nope');
If we have an array that does not exists and we check the value of the array it gives me an error. "variable is not defined"
for example I have:
var arr = new Array();
arr['house']['rooms'] = 2;
and I use
if ( typeof arr['plane']['room'] != 'undefined' ) )
it says arr['plane'] not defined...
I don't want to use this:
if ( typeof arr['plane'] != 'undefined' ) ) {
if ( typeof arr['plane']['room'] != 'undefined' ) {
}
}
In php I use isset that works nice for me, I searched a lot on google to find the answer but I can't...
The thing to realize is that there are no multi-dimensional arrays in javascript. It is easy to make an array element contain an array, and work with that, but then all references you make have to use that consideration.
So, you can do
arr = []; // or more appropriately {}, but I'll get to that soon
arr['house'] = [];
arr['house']['rooms'] = 2;
But doing
arr['house']['rooms'] = 2;
should give you an error unless you've already defined arr and arr['house'].
If you've defined arr but not arr['house'], it's valid syntax to reference arr['house'] - but the return value will (appropriately) be undefined.
And this is where you're at when you're looking at arr['plane']['room']. arr is defined, so that's ok, but arr['plane'] returns undefined, and referencing undefined.['room'] throws an error.
If you want to avoid the errors and have multiple levels of reference, you're going to have to make sure that all the levels but the lowest exist.
You're stuck with if (arr && arr['plane'] && arr['plane']['room']).
Or perhaps if (arr && arr['plane'] && room in arr['plane'] would be more accurate, depending on your needs. The first will check if arr['plane']['room'] has a truthy value, while the second will check if arr['plane']['room'] exists at all (and could have a falsey value).
Arrays vs objects
Arrays and objects are very similar and can both be accessed with [] notation, so it's slightly confusing, but technically, you're using the object aspect of the array for what you're doing. Remember, all arrays (and everything other than primitives - numbers, strings and booleans) are objects, so arrays can do everything objects can do, plus more. But arrays only work with numeric indices, i.e. arr[1][2]. When you reference an array with a string, you're attempting to access the member of the underlying object that matches that string.
But still in this case, it doesn't matter. There are no multi-dimensional arrays - or objects.
The [] notation with objects is simply a way to check for members of objects using a variable. arr['plane']['rooms'] is actually equivalent to arr.plane.rooms. but perhaps the arr.plane.room notation will help make it more clear why you have to first check arr.plane (and arr).
Use the following if you want to test for existence in an object:
if ( 'plane' in arr && 'room' in arr.plane ) {
// Do something
}
That's not an array, but an object, aka associative array. You can declare it like this:
var aarr = { house: { rooms: 2 } };
Now you can do:
if (aarr.house && aarr.house.rooms) {/* do stuff */ }
or uglier, but shorter:
if ((aarr.house || {}).rooms) {/* do stuff */ }
See also...
To more generally traverse an object to find a path in it you could use:
Object.tryPath = function(obj,path) {
path = path.split(/[.,]/);
while (path.length && obj) {
obj = obj[path.shift()];
}
return obj || null;
};
Object.tryPath(aarr,'house.rooms'); //=> 2
Object.tryPath(aarr,'house.cellar.cupboard.shelf3'); //=> null
JsFiddle
My homework is like:
Write a "keys" function that gets passed an object and returns an array of the object's properties. Be sure to screen out the object's methods. The keys array only has names of the object's name/value pairs. Because of cross browser issues(non-support in older browsers), the Objectkeys method cannot be used. Your function should provide the same service for all the browsers.
My initial code is as below:
function keys(obj){
var key="";
var i = 0;
var array = [];
for(i = 1; i<arguments.length; i++){
for(key in arguments[i]){
if(obj.hasOwnProperty&&(!_.isArray(obj))){
obj[key]=arguments[i][key];
}
}
}
for(var j = 0; j < obj.length; j++){
for(key in obj[j]){
array[j] = obj[j];
}
}
return array;
}
I'm pretty sure that my function has a lot of problems. Could you please help me with it? Thank you!
This is the solution:
function keys(obj) {
var hasOwnProperty = Object.prototype.hasOwnProperty;
var properties = [];
for (var property in obj)
if (hasOwnProperty.call(obj, property)
&& typeof obj[property] !== "function")
properties.push(property);
return properties;
}
Line by line the above code does the following:
Create an empty array properties to hold the names of all the properties of obj.
For each property property of obj do:
If the property property belongs to obj and
If obj[property] is not a function then:
Add the property property to the properties array.
Return the properties array.
See the demo: http://jsfiddle.net/qVgVn/
There are a lot of problems with your code. The answer you need is here in the MDN though: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
That function does EXACTLY what your professor asked, and it does it cross-browser. There is what is called a poly-fill, or cross-browser implementation of object.keys listed under "Compatability". Try to sort out that code to figure out what it's doing :)
Here are some problems with your own code that I see right off the bat - it's probably not working code, I just wanted to give you some guidance of things you did incorrectly:
// Name your function something useful and descriptive.
function getKeysAsArray(obj){
// For starters, dont name a variable "array" - bad practice.
var key="",
i = 0,
results = [];
// First foor loop unnecessary, do not use arguments here
// because you already know the name of your argument.
for(key in obj){
// See if this browser supports has OwnProperty by using typeof
// which will fail gracefully, vs what u did which will stop the
// script from running
if(typeof Object.hasOwnProperty === 'function'){
// You probably shouldn't be using underscore _
if(obj.hasOwnProperty && !(obj instanceof Array)){
results.push(obj[key]);
}
}
}
return results;
}
ok here i go...
function objProps(x){
var arr=[];
for (var k in x) if(typeof x[k] !='function' && x.hasOwnProperty(k)) {arr.push(k);}
return arr;
}
this code works as expected. call it with object...
get its only keys out that are NOT Functions.
I'm trying to create a multi-dimensional array.
My assumption that the following structure stuff['mykey1']['mykey2']['mykey3'] can be interpreted as stuff is an array of two-dimensional arrays. And stuff['mykey1'] will return me a two dimensional array with following keys ['mykey2']['mykey3']
I try to create this structure like so:
var stuff = null;
if(stuff === null)
{
stuff = []; // stuff is []
}
if(stuff[userId] === undefined)
{
stuff[userId] = []; // stuff is [undefined, undefined, undefined, 888087 more...]
}
if(stuff[userId][objectId] === undefined)
{
stuff[userId][objectId] = [];
}
However, when I look at stuff array as I step through, I see that after stuff[userId] = []; stuff array is [undefined, undefined, undefined, 888087 more...]
I'm expecting [888087, []]
Where do the undefined values come from?
Where do the undefined values come from?
You are using Arrays, not objects. If you add a numerical property on an Array object, it's length will be updated and the other indices stay unitialized (sparse array), but are displayed as undefined (see What is "undefined x 1" in JavaScript?).
Instead, use normal objects, where numerical properties have no special behavior:
var stuff = null;
if(stuff === null)
{
stuff = {}; // stuff is an empty object
}
if(stuff[userId] === undefined)
{
stuff[userId] = {}; // stuff is now enriched with one property
}
if(stuff[userId][objectId] === undefined)
{
stuff[userId][objectId] = {}; // or maybe you really want an array here?
}
Its because of usage of arrays. The length of the remainin elements is made undefined.
For example if a(1) is specified, a(0) will be undefined
You are trying to create associative arrays, and in JavaScript this is done with... objects, not arrays!
So at each step you need to use {} instead of [] to create the next level. And you need to use a for...in loop to iterate through the keys.
For more details, search the Web for JavaScript associative arrays". For example:
https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Working_with_Objects
The question is long answered, but I want to chip in with this shorthand, that really makes it more compact and readable:
stuff = stuff || {};
// if stuff is already defined, just leave it be. If not (||), define it as object
stuff[userId] = stuff[userId] || {};
// if stuff[userId] is already defined, define it as self (let it be unchanged). If not defined ( the || -signs ), define it as object.
stuff[userId][objectId] = stuff[userId][objectId] || {};
// and so on :)
There seems to have many question asked similar on counting number of element already but I am failing to implement them with mine problem.
After jquery ajax I get JSON data returned which looks something like this
Object {0: Object, 1: Object , xxxx:"asdf" ,yyyy:"asdf", zzzz:"asdf"}
I want to get number of object between this { } braces ( not counting those xxx,yyy element )
I tried .length which doesn't work
I also tried using this Length of a JavaScript object but that return the number of element in each object. I just want the number of object
Thank You
Try this:
var json = { 0: {}, 1: {}, xxxx: "asdf", yyyy: "asdf", zzzz: "asdf" };
function typeOf( obj ) {
return ({}).toString.call( obj )
.match(/\s([a-zA-Z]+)/)[1].toLowerCase();
}
var total = 0;
for ( var o in json ) {
if ( typeOf( json[o] ) == 'object' ) {
total++;
}
}
console.log( total ); //=> 2
Everything is an object in JavaScript. The typeof operator is misleading and won't work in this case. You can use the typeOf function above that I extracted from this blog post: Fixing the JavaScript typeof operator (worth reading). There are other ways of doing it but this seems like the most straightforward.
If it's not just a coincidence that the objects are the ones with numeric property names, and the numeric properties count up sequentially, you could do something like this:
var obj = { /* your object here */ }
for (var i=0; i in obj; i++) {
// use obj[i] for something
}
// i is now equal to the number of numeric properties
This works because as soon as i is high enough that you've run out of properties the in operator will return false. Feel free to use .hasOwnProperty() instead if you prefer.
Obviously this is a specialised solution that doesn't test the type of the different properties at all. (To actually test the type see elclanrs' solution - and either way read the page he linked to.)
Say that the entire json is in a variable called json:
var total_objects = 0;
$.each(json, function () {
if (typeof this == 'object') {
total_objects++;
}
});
However, I am curious as to why you would need to know this.
You can use a customized version from the code of this question Length of Javascript Object (ie. Associative Array) and check for element's type using typeof operator and count only those which are an object (or an array).
Object.sizeObj = function(obj) {
var size = 0, key;
for (key in obj) {
if (typeof key[obj] === 'object' && obj.hasOwnProperty(key)) size++;
}
return size;
};
// Get the count of those elements which are an object
var objectsCount = Object.sizeObj(myArray);