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);
Related
I am using angular-translate for a big application. Having several people committing code + translations, many times the translation objects are not in sync.
I am building a Grunt plugin to look at both files' structure and compare it (just the keys and overall structure, not values).
The main goals are:
Look into each file, and check if the structure of the whole object
(or file, in this case) is the exact same as the translated ones;
On error, return the key that doesn't match.
It turns out it was a bit more complicated than I anticipated. So i figured I could do something like:
Sort the object;
Check the type of data the value contains (since they are translations, it will only have strings, or objects for the nestings) and store it in another object, making the key equal to the original key and the value would be a string 'String', or an object in case it's an object. That object contains the children elements;
Recursively repeat steps 1-2 until the whole object is mapped and sorted;
Do the same for all the files
Stringify and compare everything.
A tiny example would be the following object:
{
key1: 'cool',
key2: 'cooler',
keyWhatever: {
anotherObject: {
key1: 'better',
keyX: 'awesome'
},
aObject: 'actually, it\'s a string'
},
aKey: 'more awesomeness'
}
would map to:
{
aKey: 'String',
key1: 'String',
key2: 'String',
keyWhatever: {
aObject: 'String',
anotherObject: {
key1: 'String',
keyX: 'String'
}
}
}
After this, I would stringify all the objects and proceed with a strict comparison.
My question is, is there a better way to perform this? Both in terms of simplicity and performance, since there are many translation files and they are fairly big.
I tried to look for libraries that would already do this, but I couldn't find any.
Thank you
EDIT: Thank you Jared for pointing out objects can't be sorted. I am ashamed for saying something like that :D Another solution could be iterating each of the properties on the main translation file, and in case they are strings, compare the key with the other files. In case they are objects, "enter" them, and do the same. Maybe it is even simpler than my first guess. What should be done?
Lets say you have two JSON objects, jsonA and jsonB.
function compareValues(a, b) {
//if a and b aren't the same type, they can't be equal
if (typeof a !== typeof b) {
return false;
}
// Need the truthy guard because
// typeof null === 'object'
if (a && typeof a === 'object') {
var keysA = Object.keys(a).sort(),
keysB = Object.keys(b).sort();
//if a and b are objects with different no of keys, unequal
if (keysA.length !== keysB.length) {
return false;
}
//if keys aren't all the same, unequal
if (!keysA.every(function(k, i) { return k === keysB[i];})) {
return false;
}
//recurse on the values for each key
return keysA.every(function(key) {
//if we made it here, they have identical keys
return compareValues(a[key], b[key]);
});
//for primitives just use a straight up check
} else {
return a === b;
}
}
//true if their structure, values, and keys are identical
var passed = compareValues(jsonA, jsonB);
Note that this can overflow the stack for deeply nested JSON objects. Note also that this will work for JSON but not necessarily regular JS objects as special handling is needed for Date Objects, Regexes, etc.
Actually you do need to sort the keys, as they are not required to be spit out in any particular order. Write a function,
function getComparableForObject(obj) {
var keys = Object.keys(obj);
keys.sort(a, b => a > b ? 1 : -1);
var comparable = keys.map(
key => key + ":" + getValueRepresentation(obj[key])
).join(",");
return "{" + comparable + "}";
}
Where getValueRepresentation is a function that either returns "String" or calls getComparableForObject. If you are worried about circular references, add a Symbol to the outer scope, repr, assign obj[repr] = comparable in the function above, and in getValueRepresentation check every object for a defined obj[repr] and return it instead of processing it recursively.
Sorting an array of the keys from the object works. However, sorting has an average time complexity of O(nâ‹…log(n)). We can do better. A fast general algorithm for ensuring two sets A and B are equivalent is as follows:
for item in B
if item in A
remove item from A
else
sets are not equivalent
sets are equivalent iff A is empty
To address #Katana31, we can detect circular references as we go by maintaining a set of visited objects and ensuring that all descendents of that object are not already in the list:
# poorly written pseudo-code
fn detectCycles(A, found = {})
if A in found
there is a cycle
else
found = clone(found)
add A to found
for child in A
detectCycles(child, found)
Here's a complete implementation (you can find a simplified version that assumes JSON/non-circular input here):
var hasOwn = Object.prototype.hasOwnProperty;
var indexOf = Array.prototype.indexOf;
function isObjectEmpty(obj) {
for (var key in obj) {
return false;
}
return true;
}
function copyKeys(obj) {
var newObj = {};
for (var key in obj) {
newObj[key] = undefined;
}
return newObj;
}
// compares the structure of arbitrary values
function compareObjectStructure(a, b) {
return function innerCompare(a, b, pathA, pathB) {
if (typeof a !== typeof b) {
return false;
}
if (typeof a === 'object') {
// both or neither, but not mismatched
if (Array.isArray(a) !== Array.isArray(b)) {
return false;
}
if (indexOf.call(pathA, a) !== -1 || indexOf.call(pathB, b) !== -1) {
return false;
}
pathA = pathA.slice();
pathA.push(a);
pathB = pathB.slice();
pathB.push(b);
if (Array.isArray(a)) {
// can't compare structure in array if we don't have items in both
if (!a.length || !b.length) {
return true;
}
for (var i = 1; i < a.length; i++) {
if (!innerCompare(a[0], a[i], pathA, pathA)) {
return false;
}
}
for (var i = 0; i < b.length; i++) {
if (!innerCompare(a[0], b[i], pathA, pathB)) {
return false;
}
}
return true;
}
var map = copyKeys(a), keys = Object.keys(b);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!hasOwn.call(map, key) || !innerCompare(a[key], b[key], pathA,
pathB)) {
return false;
}
delete map[key];
}
// we should've found all the keys in the map
return isObjectEmpty(map);
}
return true;
}(a, b, [], []);
}
Note that this implementation directly compares two objects for structural equivalency, but doesn't reduce the objects to a directly comparable value (like a string). I haven't done any performance testing, but I suspect that it won't add significant value, though it will remove the need to repeatedly ensure objects are non-cyclic. For that reason, you could easily split compareObjectStructure into two functions - one to compare the structure and one to check for cycles.
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)
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
I wanted to write one version of a function that iterated over both Array objects and Objects, with minimal duplicate code. Something like:
function (arr_or_obj) {
arr_or_obj.forEach( function(key,value) {
// do stuff with key and value
});
}
This was before I realized that (in Chrome at least), Object.keys returns a list of the keys for an array. So I could use that to treat the Array like an object. Like so:
function (arr_or_obj) {
var keys = Object.keys(arr_or_obj);
keys.forEach( function(key) {
var value = arr_or_obj[key];
// do stuff with key and value
});
}
Problem solved. But this was not before I wrote my own "JavaScript pseudo-Array iterator". The only advantage of this was that instead of getting a list of keys for the Array (which could be very long), we save memory by producing only the return values we need. Like so:
function create_iterator(max) {
var jkeys = {};
jkeys.count_ = 0;
jkeys.length = max;
jkeys.getter_ = function() {
var returnable = jkeys.count_;
jkeys.count_ += 1;
jkeys.__defineGetter__(jkeys.count_,jkeys.getter_);
delete jkeys[jkeys.count_-1];
return returnable;
};
jkeys.__defineGetter__(0, jkeys.getter_);
return jkeys;
}
Which you can then call by going:
var z = create_iterator(100);
z[0];
>> 0
z[0];
>> undefined
z[1];
>> 1;
z[2]
>> 2;
...
This is sort of a question and answer in one, but the obvious question is, is there a better way to do this without using Object.keys?
Object.keys gives you an array of the object's own enumerable properties. That is, properties it has itself, not from its prototype, and that are enumerable (so not including things like length on arrays, which is a non-enumerable property).
You can do the same thing with for-in if you use correct safeguards:
var key;
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key)) {
// do something with it
}
}
Now you're doing the same thing you were doing with Object.keys.
But, note that this (and Object.keys) will include properties people put on arrays that aren't array indexes (something which is perfectly valid in JavaScript). E.g.:
var key;
var a = [1, 2, 3];
a.foo = "bar";
for (key in a) {
if (a.hasOwnProperty(key)) {
console.log(key); // Will show "foo" as well as "0", "1", and "2"
}
}
If you want to only process array indexes and not other properties, you'll need to know whether the thing is an array, and then if so use the "is this an index" test:
var key;
var isArray = Array.isArray(arr_or_obj);
// Or (see below):
//var isArray = Object.prototype.toString.call(arr_or_obj) === "[object Array]";
for (key in arr_or_obj) {
if (a.hasOwnProperty(key) &&
(!isArray || isArrayIndex(key))
) {
console.log(key); // Will only show "0", "1", and "2" for `a` above
}
}
...where Array.isArray is a new feature of ES5 which is easily shimmed for older browsers if necessary:
if (!Array.isArray) {
Array.isArray = (function() {
var toString = Object.prototype.toString;
function isArray(arg) {
return toString.call(arg) === "[object Array]";
}
return isArray;
})();
}
...and isArrayIndex is:
function isArrayIndex(key) {
return /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294; // 2^32 - 2
}
See this other answer for details on that (where the magic numbers come from, etc.).
That loop above will loop through an object's own enumerable properties (all of them) if it's not an array, and loop through the indexes (but not other properties) if it's an array.
Taking it a step further, what if you want to include the properties the object gets from its prototype? Object.keys omits those (by design). If you want to include them, just don't use hasOwnProperty in the for-in loop.
You can try something like this...
Live Demo
function forIn(obj) {
var isArray = Array.isArray(obj),
temp = isArray ? obj : Object.keys(obj);
temp.forEach(function (value, index, array) {
console.log(this[isArray ? index : value]);
}, obj);
}
You can write:
for (key in arr_or_obj) {
if (arr_or_obj.hasOwnProperty(key) {
value = arr_or_obj[key];
// do stuff with key and value
}
}
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.