JS checking deep object property existence [duplicate] - javascript

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 5 years ago.
I'm trying to find an elegant way to check the if certain deep properties exist in an object. So practically trying to avoid monstrous protective checks for undefined eg.
if ((typeof error !== 'undefined') &&
(typeof error.responseJSON !== 'undefined') &&
(typeof error.responseJSON.error) &&
(typeof error.responseJSON.error.message)) {
errorMessage = error.responseJSON.error.message;
}
What I'm thinking about is a convenience-function like
if (exists(error.responseJSON.error.message)) { ... }
Any ideas? For convenience, the use of underscore-library is ok for the solution.

There are several possiblities:
Try-catch
try {
errorMessage = error.responseJSON.error.message;
} catch(e) { /* ignore the error */}
Fails for:
Object.defineProperty(error, 'responseJSON', {
get: function() { throw new Error('This will not be shown')
});
&&
errorMessage = error && error.responseJSON && error.responseJSON.error && error.responseJSON.error.message;
Fails for:
error.responseJSON = 0;
// errorMessage === 0 instead of undefined
function
function getDeepProperty(obj,propstr) {
var prop = propstr.split('.');
for (var i=0; i<prop.length; i++) {
if (typeof obj === 'object')
obj = obj[prop[i]];
}
return obj;
}
errorMessage = getDeepProperty(error, 'responseJSON.error.message');
// you could put it all in a string, if the object is defined in the window scope
Fails for:
// It's hard(er) to use
function alternative - see comment by #Olical
function getDeepProperty(obj) {
for (var i=1; i<arguments.length; i++) {
if (typeof obj === 'object')
obj = obj[arguments[i]];
}
return obj;
}
errorMessage = getDeepProperty(error, 'responseJSON', 'error', 'message');

Try this underscore mixin to look up a variable with a path. It takes an object and string and t
_.mixin({
lookup: function (obj, key) {
var type = typeof key;
if (type == 'string' || type == "number")
key = ("" + key).replace(/\[(.*?)\]/, function (m, key) { //handle case where [1] may occur
return '.' + key.replace(/["']/g, ""); //strip quotes
}).split('.');
for (var i = 0, l = key.length; i < l; i++) {
if (_.has(obj, key[i]))
obj = obj[key[i]];
else
return undefined;
}
return obj;
}
});
Now call in your example:
_.lookup(error, 'responseJSON.error.message') // returns responseJSON.error.message if it exists otherwise `undefined`

Related

Compare two objects based on property names [duplicate]

I am using node, mocha, and chai for my application. I want to test that my returned results data property is the same "type of object" as one of my model objects (Very similar to chai's instance). I just want to confirm that the two objects have the same sets of property names. I am specifically not interested in the actual values of the properties.
Let's say I have the model Person like below. I want to check that my results.data has all the same properties as the expected model does. So in this case, Person which has a firstName and lastName.
So if results.data.lastName and results.data.firstName both exist, then it should return true. If either one doesn't exist, it should return false. A bonus would be if results.data has any additional properties like results.data.surname, then it would return false because surname doesn't exist in Person.
This model
function Person(data) {
var self = this;
self.firstName = "unknown";
self.lastName = "unknown";
if (typeof data != "undefined") {
self.firstName = data.firstName;
self.lastName = data.lastName;
}
}
You can serialize simple data to check for equality:
data1 = {firstName: 'John', lastName: 'Smith'};
data2 = {firstName: 'Jane', lastName: 'Smith'};
JSON.stringify(data1) === JSON.stringify(data2)
This will give you something like
'{firstName:"John",lastName:"Smith"}' === '{firstName:"Jane",lastName:"Smith"}'
As a function...
function compare(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
compare(data1, data2);
EDIT
If you're using chai like you say, check out http://chaijs.com/api/bdd/#equal-section
EDIT 2
If you just want to check keys...
function compareKeys(a, b) {
var aKeys = Object.keys(a).sort();
var bKeys = Object.keys(b).sort();
return JSON.stringify(aKeys) === JSON.stringify(bKeys);
}
should do it.
2 Here a short ES6 variadic version:
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), []);
const union = new Set(allKeys);
return objects.every(object => union.size === Object.keys(object).length);
}
A little performance test (MacBook Pro - 2,8 GHz Intel Core i7, Node 5.5.0):
var x = {};
var y = {};
for (var i = 0; i < 5000000; ++i) {
x[i] = i;
y[i] = i;
}
Results:
objectsHaveSameKeys(x, y) // took 4996 milliseconds
compareKeys(x, y) // took 14880 milliseconds
hasSameProps(x,y) // after 10 minutes I stopped execution
If you want to check if both objects have the same properties name, you can do this:
function hasSameProps( obj1, obj2 ) {
return Object.keys( obj1 ).every( function( prop ) {
return obj2.hasOwnProperty( prop );
});
}
var obj1 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] },
obj2 = { prop1: 'hello', prop2: 'world', prop3: [1,2,3,4,5] };
console.log(hasSameProps(obj1, obj2));
In this way you are sure to check only iterable and accessible properties of both the objects.
EDIT - 2013.04.26:
The previous function can be rewritten in the following way:
function hasSameProps( obj1, obj2 ) {
var obj1Props = Object.keys( obj1 ),
obj2Props = Object.keys( obj2 );
if ( obj1Props.length == obj2Props.length ) {
return obj1Props.every( function( prop ) {
return obj2Props.indexOf( prop ) >= 0;
});
}
return false;
}
In this way we check that both the objects have the same number of properties (otherwise the objects haven't the same properties, and we must return a logical false) then, if the number matches, we go to check if they have the same properties.
Bonus
A possible enhancement could be to introduce also a type checking to enforce the match on every property.
If you want deep validation like #speculees, here's an answer using deep-keys (disclosure: I'm sort of a maintainer of this small package)
// obj1 should have all of obj2's properties
var deepKeys = require('deep-keys');
var _ = require('underscore');
assert(0 === _.difference(deepKeys(obj2), deepKeys(obj1)).length);
// obj1 should have exactly obj2's properties
var deepKeys = require('deep-keys');
var _ = require('lodash');
assert(0 === _.xor(deepKeys(obj2), deepKeys(obj1)).length);
or with chai:
var expect = require('chai').expect;
var deepKeys = require('deep-keys');
// obj1 should have all of obj2's properties
expect(deepKeys(obj1)).to.include.members(deepKeys(obj2));
// obj1 should have exactly obj2's properties
expect(deepKeys(obj1)).to.have.members(deepKeys(obj2));
Here's a deep-check version of the function provided above by schirrmacher.
Below is my attempt. Please note:
Solution does not check for null and is not bullet proof
I haven't performance tested it. Maybe schirrmacher or OP can do that and share for the community.
I'm not a JS expert :).
function objectsHaveSameKeys(...objects) {
const allKeys = objects.reduce((keys, object) => keys.concat(Object.keys(object)), [])
const union = new Set(allKeys)
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Update 1
A 90% improvement on the recursive deep-check version is achieved on my computer by skipping the concat() and adding the keys directly to the Set(). The same optimization to the original single level version by schirrmacher also achieves ~40% improvement.
The optimized deep-check is now very similar in performance to the optimized single level version!
function objectsHaveSameKeysOptimized(...objects) {
let union = new Set();
union = objects.reduce((keys, object) => keys.add(Object.keys(object)), union);
if (union.size === 0) return true
if (!objects.every((object) => union.size === Object.keys(object).length)) return false
for (let key of union.keys()) {
let res = objects.map((o) => (typeof o[key] === 'object' ? o[key] : {}))
if (!objectsHaveSameKeys(...res)) return false
}
return true
}
Performance Comparison
var x = {}
var y = {}
var a = {}
for (var j = 0; j < 10; ++j){
a[j] = j
}
for (var i = 0; i < 500000; ++i) {
x[i] = JSON.parse(JSON.stringify(a))
y[i] = JSON.parse(JSON.stringify(a))
}
let startTs = new Date()
let result = objectsHaveSameKeys(x, y)
let endTs = new Date()
console.log('objectsHaveSameKeys = ' + (endTs - startTs)/1000)
Results
A: Recursive/deep-check versions*
objectsHaveSameKeys = 5.185
objectsHaveSameKeysOptimized = 0.415
B: Original non-deep versions
objectsHaveSameKeysOriginalNonDeep = 0.517
objectsHaveSameKeysOriginalNonDeepOptimized = 0.342
function getObjectProperties(object, propertiesString = '') {
let auxPropertiesString = propertiesString;
for (const objectLevel of Object.keys(object).sort((a, b) => a.localeCompare(b))) {
if (typeof object[objectLevel] === 'object') {
auxPropertiesString += getObjectProperties(object[objectLevel], auxPropertiesString);
} else {
auxPropertiesString += objectLevel;
}
}
return auxPropertiesString;
}
function objectsHaveTheSameKeys(objects) {
const properties = [];
for (const object of objects) {
properties.push(getObjectProperties(object));
}
return properties.every(eachProperty => eachProperty === properties[0]);
}
It's a bit rudimentary, but should do the work in case you want to compare properties.
Legacy Browser Object Compare Function
Unlike the other solutions posted here, my Object Compare Function works in ALL BROWSERS, modern or legacy, including very old browsers, even Internet Explorer 5 (c.2000)!
Features:
Can compare an unlimited list of Objects. All must match or fails!
Ignores property order
Only compares "own" properties (i.e. non-prototype)
Matches BOTH property names and property values (key-value pairs)!
Matches functions signatures in objects!
Every object submitted is cross-compared with each other to detect missing properties in cases where one is missing but not in the other
Avoids null, undefined, NaN, Arrays, non-Objects, etc.
{} empty object detection
Works in almost ALL BROWSERS, including even Internet Explorer 5 and many other legacy browsers!
Note the function does not detect complex objects in properties, but you could rewrite the function to call them recursively.
Just call the method with as many objects as you like!
ObjectCompare(myObject1,myObject2,myObject3)
function ObjectCompare() {
try {
if (arguments && arguments.length > 0) {
var len = arguments.length;
if (len > 1) {
var array = [];
for (var i = 0; i < len; i++) {
if (
((typeof arguments[i] !== 'undefined') || (typeof arguments[i] === 'undefined' && arguments[i] !== undefined))
&& (arguments[i] !== null)
&& !(arguments[i] instanceof Array)
&& ((typeof arguments[i] === 'object') || (arguments[i] instanceof Object))
) {
array.push(arguments[i]);
}
}
if (array.length > 1) {
var a1 = array.slice();
var a2 = array.slice();
var len1 = a1.length;
var len2 = a2.length;
var noKeys = true;
var allKeysMatch = true;
for (var x = 0; x < len1; x++) {
console.log('---------- Start Object Check ---------');
//if (len2>0) {
// a2.shift();// remove next item
//}
len2 = a2.length;
if (len2 > 0 && allKeysMatch) {
for (var y = 0; y < len2; y++) {
if (x !== y) {// ignore objects checking themselves
//console.log('Object1: ' + JSON.stringify(a1[x]));
//console.log('Object2: ' + JSON.stringify(a2[y]));
console.log('Object1: ' + a1[x].toString());
console.log('Object2: ' + a2[y].toString());
var ownKeyCount1 = 0;
for (var key1 in a1[x]) {
if (a1[x].hasOwnProperty(key1)) {
// ---------- valid property to check ----------
ownKeyCount1++;
noKeys = false;
allKeysMatch = false;// prove all keys match!
var ownKeyCount2 = 0;
for (var key2 in a2[y]) {
if (a2[y].hasOwnProperty(key2) && !allKeysMatch) {
ownKeyCount2++;
if (key1 !== key1 && key2 !== key2) {// NaN check
allKeysMatch = true;// proven
break;
} else if (key1 === key2) {
if (a1[x][key1].toString() === a2[y][key2].toString()) {
allKeysMatch = true;// proven
console.log('KeyValueMatch=true : ' + key1 + ':' + a1[x][key1] + ' | ' + key2 + ':' + a2[y][key2]);
break;
}
}
}
}
if (ownKeyCount2 === 0) {// if second objects has no keys end early
console.log('-------------- End Check -------------');
return false;
}
// ---------------------------------------------
}
}
console.log('-------------- End Check -------------');
}
}
}
}
console.log('---------------------------------------');
if (noKeys || allKeysMatch) {
// If no keys in any objects, assume all objects are {} empty and so the same.
// If all keys match without errors, then all object match.
return true;
} else {
return false;
}
}
}
console.log('---------------------------------------');
return true;// one object
}
console.log('---------------------------------------');
return false;// no objects
} catch (e) {
if (typeof console !== 'undefined' && console.error) {
console.error('ERROR : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.warn) {
console.warn('WARNING : Function ObjectCompare() : ' + e);
} else if (typeof console !== 'undefined' && console.log) {
console.log('ERROR : Function ObjectCompare() : ' + e);
}
return false;
}
}
// TESTING...
var myObject1 = new Object({test: 1, item: 'hello', name: 'john', f: function(){var x=1;}});
var myObject2 = new Object({item: 'hello', name: 'john', test: 1, f: function(){var x=1;}});
var myObject3 = new Object({name: 'john', test: 1, item: 'hello', f: function(){var x=1;}});
// RETURNS TRUE
//console.log('DO ALL OBJECTS MATCH? ' + ObjectCompare(myObject1, myObject2, myObject3));
If you are using underscoreJs then you can simply use _.isEqual function
and it compares all keys and values at each and every level of hierarchy like below example.
var object = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
var object1 = {"status":"inserted","id":"5799acb792b0525e05ba074c","data":{"workout":[{"set":[{"setNo":1,"exercises":[{"name":"hjkh","type":"Reps","category":"Cardio","set":{"reps":5}}],"isLastSet":false,"index":0,"isStart":true,"startDuration":1469689001989,"isEnd":true,"endDuration":1469689003323,"speed":"00:00:01"}],"setType":"Set","isSuper":false,"index":0}],"time":"2016-07-28T06:56:52.800Z"}};
console.log(_.isEqual(object, object1));//return true
If all the keys and values for those keys are same in both the objects then it will return true, otherwise return false.
Here is my attempt at validating JSON properties. I used #casey-foster 's approach, but added recursion for deeper validation. The third parameter in function is optional and only used for testing.
//compare json2 to json1
function isValidJson(json1, json2, showInConsole) {
if (!showInConsole)
showInConsole = false;
var aKeys = Object.keys(json1).sort();
var bKeys = Object.keys(json2).sort();
for (var i = 0; i < aKeys.length; i++) {
if (showInConsole)
console.log("---------" + JSON.stringify(aKeys[i]) + " " + JSON.stringify(bKeys[i]))
if (JSON.stringify(aKeys[i]) === JSON.stringify(bKeys[i])) {
if (typeof json1[aKeys[i]] === 'object'){ // contains another obj
if (showInConsole)
console.log("Entering " + JSON.stringify(aKeys[i]))
if (!isValidJson(json1[aKeys[i]], json2[bKeys[i]], showInConsole))
return false; // if recursive validation fails
if (showInConsole)
console.log("Leaving " + JSON.stringify(aKeys[i]))
}
} else {
console.warn("validation failed at " + aKeys[i]);
return false; // if attribute names dont mactch
}
}
return true;
}

Javascript what is the difference between var = null, and var = {} [duplicate]

This question already has answers here:
How do I test for an empty JavaScript object?
(48 answers)
Closed 5 years ago.
What is the fastest way to check if an object is empty or not?
Is there a faster and better way than this:
function count_obj(obj){
var i = 0;
for(var key in obj){
++i;
}
return i;
}
For ECMAScript5 (not supported in all browsers yet though), you can use:
Object.keys(obj).length === 0
I'm assuming that by empty you mean "has no properties of its own".
// Speed up calls to hasOwnProperty
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isEmpty(obj) {
// null and undefined are "empty"
if (obj == null) return true;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// If it isn't an object at this point
// it is empty, but it can't be anything *but* empty
// Is it empty? Depends on your application.
if (typeof obj !== "object") return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
Examples:
isEmpty(""), // true
isEmpty(33), // true (arguably could be a TypeError)
isEmpty([]), // true
isEmpty({}), // true
isEmpty({length: 0, custom_property: []}), // true
isEmpty("Hello"), // false
isEmpty([1,2,3]), // false
isEmpty({test: 1}), // false
isEmpty({length: 3, custom_property: [1,2,3]}) // false
If you only need to handle ECMAScript5 browsers, you can use Object.getOwnPropertyNames instead of the hasOwnProperty loop:
if (Object.getOwnPropertyNames(obj).length > 0) return false;
This will ensure that even if the object only has non-enumerable properties isEmpty will still give you the correct results.
EDIT: Note that you should probably use ES5 solution instead of this since ES5 support is widespread these days. It still works for jQuery though.
Easy and cross-browser way is by using jQuery.isEmptyObject:
if ($.isEmptyObject(obj))
{
// do something
}
More: http://api.jquery.com/jQuery.isEmptyObject/
You need jquery though.
Underscore and lodash each have a convenient isEmpty() function, if you don't mind adding an extra library.
_.isEmpty({});
Lets put this baby to bed; tested under Node, Chrome, Firefox and IE 9, it becomes evident that for most use cases:
(for...in...) is the fastest option to use!
Object.keys(obj).length is 10 times slower for empty objects
JSON.stringify(obj).length is always the slowest (not surprising)
Object.getOwnPropertyNames(obj).length takes longer than Object.keys(obj).length can be much longer on some systems.
Bottom line performance wise, use:
function isEmpty(obj) {
for (var x in obj) { return false; }
return true;
}
or
function isEmpty(obj) {
for (var x in obj) { if (obj.hasOwnProperty(x)) return false; }
return true;
}
Results under Node:
first result: return (Object.keys(obj).length === 0)
second result: for (var x in obj) { return false; }...
third result: for (var x in obj) { if (obj.hasOwnProperty(x)) return false; }...
forth result: return ('{}' === JSON.stringify(obj))
Testing for Object with 0 keys
0.00018
0.000015
0.000015
0.000324
Testing for Object with 1 keys
0.000346
0.000458
0.000577
0.000657
Testing for Object with 2 keys
0.000375
0.00046
0.000565
0.000773
Testing for Object with 3 keys
0.000406
0.000476
0.000577
0.000904
Testing for Object with 4 keys
0.000435
0.000487
0.000589
0.001031
Testing for Object with 5 keys
0.000465
0.000501
0.000604
0.001148
Testing for Object with 6 keys
0.000492
0.000511
0.000618
0.001269
Testing for Object with 7 keys
0.000528
0.000527
0.000637
0.00138
Testing for Object with 8 keys
0.000565
0.000538
0.000647
0.00159
Testing for Object with 100 keys
0.003718
0.00243
0.002535
0.01381
Testing for Object with 1000 keys
0.0337
0.0193
0.0194
0.1337
Note that if your typical use case tests a non empty object with few keys, and rarely do you get to test empty objects or objects with 10 or more keys, consider the Object.keys(obj).length option. - otherwise go with the more generic (for... in...) implementation.
Note that Firefox seem to have a faster support for Object.keys(obj).length and Object.getOwnPropertyNames(obj).length, making it a better choice for any non empty Object, but still when it comes to empty objects, the (for...in...) is simply 10 times faster.
My 2 cents is that Object.keys(obj).length is a poor idea since it creates an object of keys just to count how many keys are inside, than destroys it! In order to create that object he needs to loop overt the keys... so why use it and not the (for... in...) option :)
var a = {};
function timeit(func,count) {
if (!count) count = 100000;
var start = Date.now();
for (i=0;i<count;i++) func();
var end = Date.now();
var duration = end - start;
console.log(duration/count)
}
function isEmpty1() {
return (Object.keys(a).length === 0)
}
function isEmpty2() {
for (x in a) { return false; }
return true;
}
function isEmpty3() {
for (x in a) { if (a.hasOwnProperty(x)) return false; }
return true;
}
function isEmpty4() {
return ('{}' === JSON.stringify(a))
}
for (var j=0;j<10;j++) {
a = {}
for (var i=0;i<j;i++) a[i] = i;
console.log('Testing for Object with '+Object.keys(a).length+' keys')
timeit(isEmpty1);
timeit(isEmpty2);
timeit(isEmpty3);
timeit(isEmpty4);
}
a = {}
for (var i=0;i<100;i++) a[i] = i;
console.log('Testing for Object with '+Object.keys(a).length+' keys')
timeit(isEmpty1);
timeit(isEmpty2);
timeit(isEmpty3);
timeit(isEmpty4, 10000);
a = {}
for (var i=0;i<1000;i++) a[i] = i;
console.log('Testing for Object with '+Object.keys(a).length+' keys')
timeit(isEmpty1,10000);
timeit(isEmpty2,10000);
timeit(isEmpty3,10000);
timeit(isEmpty4,10000);
Elegant way - use keys
var myEmptyObj = {};
var myFullObj = {"key":"value"};
console.log(Object.keys(myEmptyObj).length); //0
console.log(Object.keys(myFullObj).length); //1
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
function isEmpty( o ) {
for ( var p in o ) {
if ( o.hasOwnProperty( p ) ) { return false; }
}
return true;
}
var x= {}
var y= {x:'hi'}
console.log(Object.keys(x).length===0)
console.log(Object.keys(y).length===0)
true
false
http://jsfiddle.net/j7ona6hz/1/
Surprised to see so many weak answers on such a basic JS question... The top answer is no good too for these reasons:
it generates a global variable
returns true on undefined
uses for...in which is extremely slow by itself
function inside for...in is useless - return false without hasOwnProperty magic will work fine
In fact there's a simpler solution:
function isEmpty(value) {
return Boolean(value && typeof value === 'object') && !Object.keys(value).length;
}
https://lodash.com/docs#isEmpty comes in pretty handy:
_.isEmpty({}) // true
_.isEmpty() // true
_.isEmpty(null) // true
_.isEmpty("") // true
How bad is this?
function(obj){
for(var key in obj){
return false; // not empty
}
return true; // empty
}
No need for a library.
function(){ //must be within a function
var obj = {}; //the object to test
for(var isNotEmpty in obj) //will loop through once if there is a property of some sort, then
return alert('not empty')//what ever you are trying to do once
return alert('empty'); //nope obj was empty do this instead;
}
It might be a bit hacky. You can try this.
if (JSON.stringify(data).length === 2) {
// Do something
}
Not sure if there is any disadvantage of this method.
fast onliner for 'dictionary'-objects:
function isEmptyDict(d){for (var k in d) return false; return true}
You can write a fallback if Array.isArray and Object.getOwnPropertyNames is not available
XX.isEmpty = function(a){
if(Array.isArray(a)){
return (a.length==0);
}
if(!a){
return true;
}
if(a instanceof Object){
if(a instanceof Date){
return false;
}
if(Object.getOwnPropertyNames(a).length == 0){
return true;
}
}
return false;
}
Imagine you have the objects below:
var obj1= {};
var obj2= {test: "test"};
Don't forget we can NOT use === sign for testing an object equality as they get inheritance, so If you using ECMA 5 and upper version of javascript, the answer is easy, you can use the function below:
function isEmpty(obj) {
//check if it's an Obj first
var isObj = obj !== null
&& typeof obj === 'object'
&& Object.prototype.toString.call(obj) === '[object Object]';
if (isObj) {
for (var o in obj) {
if (obj.hasOwnProperty(o)) {
return false;
break;
}
}
return true;
} else {
console.error("isEmpty function only accept an Object");
}
}
so the result as below:
isEmpty(obj1); //this returns true
isEmpty(obj2); //this returns false
isEmpty([]); // log in console: isEmpty function only accept an Object
funtion isEmpty(o,i)
{
for(i in o)
{
return!1
}
return!0
}
here's a good way to do it
function isEmpty(obj) {
if (Array.isArray(obj)) {
return obj.length === 0;
} else if (typeof obj === 'object') {
for (var i in obj) {
return false;
}
return true;
} else {
return !obj;
}
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isArray(a) {
return Object.prototype.toString.call(a) === '[object Array]'
}
function isObject(a) {
return Object.prototype.toString.call(a) === '[object Object]'
}
function isEmpty(a) {
if (null == a || "" == a)return!0;
if ("number" == typeof a || "string" == typeof a)return!1;
var b = !0;
if (isArray(a)) {
if (!a.length)return!0;
for (var c = 0; c < a.length; c++)isEmpty(a[c]) || (b = !1);
return b
}
if (isObject(a)) {
for (var d in a)hasOwnProperty.call(a, d) && (isEmpty(a[d]) || (b = !1));
return b
}
return!0
}
May be you can use this decision:
var isEmpty = function(obj) {
for (var key in obj)
if(obj.hasOwnProperty(key))
return false;
return true;
}
I modified Sean Vieira's code to suit my needs. null and undefined don't count as object at all, and numbers, boolean values and empty strings return false.
'use strict';
// Speed up calls to hasOwnProperty
var hasOwnProperty = Object.prototype.hasOwnProperty;
var isObjectEmpty = function(obj) {
// null and undefined are not empty
if (obj == null) return false;
if(obj === false) return false;
if(obj === true) return false;
if(obj === "") return false;
if(typeof obj === "number") {
return false;
}
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
};
exports.isObjectEmpty = isObjectEmpty;
here my solution
function isEmpty(value) {
if(Object.prototype.toString.call(value) === '[object Array]') {
return value.length == 0;
} else if(value != null && typeof value === 'object') {
return Object.getOwnPropertyNames(value).length == 0;
} else {
return !(value || (value === 0));
}
}
Chears
if (Object.getOwnPropertyNames(obj1).length > 0)
{
alert('obj1 is empty!');
}

Another way to achieve Object.Assign() in JavaScript

Object.assign(home, action.home.fields);
I'm assigning action.home.fields to my home object. Found out that the Object.assign isn't supported in IE.
What's the equivalent to what I'm doing that's support in IE?
I read the spread operator? So would:
home = {...home, ...action.home.fields};
work?
I did check the support on that, wasn't support massively either.
This is the not-so-official polyfill for Object.assign():
if (typeof Object.assign != 'function') {
(function () {
Object.assign = function (target) {
'use strict';
// We must check against these specific cases.
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
})();
}
Taken from MDN.

Better way to see if an object has another object and a property [duplicate]

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 8 years ago.
I have this method. Is there a way to write it better? What I want to check if he trigger of analyticsTracker is available.
isTriggerAvailable: function() {
return (
typeof VC !== 'undefined' &&
typeof VC.components !== 'undefined' &&
typeof VC.components.analyticsTracker !== 'undefined' &&
typeof VC.components.analyticsTracker.trigger !== 'undefined'
);
},
You can use the function described here.
function checkNested(obj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
for (var i = 0; i < args.length; i++) {
if (!obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
https://stackoverflow.com/a/4676258/2134720

Resolve circular references from JSON object

If I have a serialized JSON from json.net like so:
User:{id:1,{Foo{id:1,prop:1}},
FooList{$ref: "1",Foo{id:2,prop:13}}
I want to have knockout output a foreach over FooList but I am not sure how to proceed because the $ref things could throw things.
I'm thinking the solution would be to somehow force all the Foos to be rendered in the FooList by not using:
PreserveReferencesHandling = PreserveReferencesHandling.Objects
but that seems wasteful..
I've found some bugs and implemented arrays support:
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++)
// check also if the array element is not a primitive value
if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
continue;
else if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
return obj;
}
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj);
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i = 0; i < refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[ref[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
The json object which you are receiving from the server contains Circular References. Before using the object you should have to first remove all the $ref properties from the object, means in place of $ref : "1" you have to put the object which this link points.
In your case may be it is pointing to the User's object whose id is 1
For this you should check out Douglas Crockfords Plugin on github.There is a cycle.js which can do the job for you.
or you can use the following code (not tested) :
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj)
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i=0; i<refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[refs[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
Let me know if it helps !
This is actually extremely simple if you take advantage of JSON.parse's reviver parameter.
Example below. See browser console for the output because StackOverflow's snippet console output will not provide an accurate picture of what the result is.
// example JSON
var j = '{"$id":"0","name":"Parent",' +
'"child":{"$id":"1", "name":"Child","parent":{"$ref":"0"}},' +
'"nullValue":null}';
function parseAndResolve(json) {
var refMap = {};
return JSON.parse(json, function (key, value) {
if (key === '$id') {
refMap[value] = this;
// return undefined so that the property is deleted
return void(0);
}
if (value && value.$ref) { return refMap[value.$ref]; }
return value;
});
}
console.log(parseAndResolve(j));
<b>See actual browser console for output.</b>
I had trouble with the array correction in the answer of Alexander Vasiliev.
I can't comment his answer (don't own enough reputations points ;-) ), so I had to add a new answer...
(where I had a popup as best practice not to answer on other answers and only on the original question - bof)
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++) {
// check also if the array element is not a primitive value
if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
return obj[i];
if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
}
return obj;
}
In the accepted implementation, if you're inspecting an array and come across a primitive value, you will return that value and overwrite that array. You want to instead continue inspecting all of the elements of the array and return the array at the end.
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++)
// check also if the array element is not a primitive value
if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
continue;
else if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
return obj;
}
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj);
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i = 0; i < refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[ref[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
my solution(works for arrays as well):
usage: rebuildJsonDotNetObj(jsonDotNetResponse)
The code:
function rebuildJsonDotNetObj(obj) {
var arr = [];
buildRefArray(obj, arr);
return setReferences(obj, arr)
}
function buildRefArray(obj, arr) {
if (!obj || obj['$ref'])
return;
var objId = obj['$id'];
if (!objId)
{
obj['$id'] = "x";
return;
}
var id = parseInt(objId);
var array = obj['$values'];
if (array && Array.isArray(array)) {
arr[id] = array;
array.forEach(function (elem) {
if (typeof elem === "object")
buildRefArray(elem, arr);
});
}
else {
arr[id] = obj;
for (var prop in obj) {
if (typeof obj[prop] === "object") {
buildRefArray(obj[prop], arr);
}
}
}
}
function setReferences(obj, arrRefs) {
if (!obj)
return obj;
var ref = obj['$ref'];
if (ref)
return arrRefs[parseInt(ref)];
if (!obj['$id']) //already visited
return obj;
var array = obj['$values'];
if (array && Array.isArray(array)) {
for (var i = 0; i < array.length; ++i)
array[i] = setReferences(array[i], arrRefs)
return array;
}
for (var prop in obj)
if (typeof obj[prop] === "object")
obj[prop] = setReferences(obj[prop], arrRefs)
delete obj['$id'];
return obj;
}

Categories