How to determine if a javascript object is simple or complex? - javascript

Basically I need to tell apart the following two:
var simple = 5 // or "word", or 56.78, or any other "simple" object
var complex = {propname: "propvalue", "otherprop": "othervalue"}

Using typeof operator you can determine the following:
"number" Operand is a number
"string" Operand is a string
"boolean" Operand is a Boolean
"object" Operand is an object
"undefined" Operand is not defined.
Edited:
As it was suggested in a comment you may want to also check if value is null, as typeof null will return object.

You could use typeof:
typeof 5 == "number";
typeof 1.5 == "number";
typeof true == "boolean";
typeof "word" == "string";
typeof {} == "object";
Basically:
if(obj == null) {
//null or undefined
}
else if(typeof obj == "object") {
//It's "complex"
}
else {
//Primitive or "simple"
}
Note: null will return "object", so you need to check for it.

The problem is that more than just {} returns a type of 'object'
typeof 5 == 'number'
typeof NaN == 'number'
typeof 'test' == 'string'
typeof true == 'boolean'
typeof undefined == 'undefined'
typeof null == 'object'
typeof /asdf/ == 'object' // this is true in some engines, like Firefox's. Not in V8 (in which it is 'function')
typeof [] == 'object'
typeof {} == 'object'
But, by using toString you can check further:
toString.call(null) == '[object Window]' // or '[object global]' or '[object Null]' - depends on engine
toString.call(/asdf/) == '[object RegExp]'
toString.call([]) == '[object Array]'
toString.call({}) == '[object Object]'
So, the best way to check is:
var test;
test = {};
typeof test == 'object' && toString.call(test) == '[object Object]'; // true
test = [];
typeof test == 'object' && toString.call(test) == '[object Object]'; // false
// et cetera
Hope that helps

Credit here
Object.prototype.getName = function() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
};
var simple = 5; // or "word", or 56.78, or any other "simple" object
var complex = { propname : "propvalue"
, "otherprop" : "othervalue"
};
simple.getName(); // returns: "Number"
complex.getName(); // returns: "Object"

Try the following
if (typeof obj === 'object') {
// It's complex
} else {
// It's not
}

In your case:
var simple = 5; // number, not an object
var simple = new Number(5); // now it is an object, but still the value is 5
var complex = {propname: "propvalue", "otherprop": "othervalue"};
for ( property in complex ) {
if ( complex.hasOwnProperty( property ) )
{
alert ( 'composite object' );
return;
} else {
alert ( 'simple object' );
return;
}
}
As of what I understand from you question - you need to tell if the object has properties/methods.

You could just make a simple function that returns true for simple types:
function isSimple( a ) {
return (typeof a).match(/(number)|(boolean)|(string)/)
}
Note that this will return true for NaN as it's considered a number, and false for 'undefined' - but you could easily modify this to suit your specific case.
Run the snippet below to see it in action
<script>
// return true/false if typeof matches simple regex pattern
function isSimple( a ) {
return (typeof a).match(/(number)|(boolean)|(string)/);
}
// setup some tests cases
var tests = [
[1,2,3],
'hello',
7,
{ foo: 'bar' },
NaN
]
// log output of isSimple function against each test case
for( var i in tests ) {
var value = tests[ i ];
if( isSimple( value ) ) {
console.log( 'simple value', value );
} else {
console.log( 'not simple', value );
}
}
</script>

Related

How to check if value is empty in nested object

For condition rendering, i'm checking if my object get an empty value.
I'm using "useState" to pass each object value to "isEmpty".
First I'm creating a new object because i delete the value " SummaryOfChanges" who can be empty.
After, I'm using "some" to get every value and pass every value to 'isEmpty'
//USE EFFECT
useEffect(() => {
if (offerData) {
let myObject = { ...offerData };
const { SummaryOfChanges, ...newObject } = myObject;
if (Object.values(newObject)?.some((x) => isEmpty(x))) {
setCheckIfEmpty(true);
} else {
setCheckIfEmpty(false);
}
}
}, []);
//ISEMPTY
export const isEmpty = (value) => {
return (
value === undefined ||
value === null ||
(typeof value === 'object' && Object.keys(value).length === 0) ||
Object.keys(value) === undefined ||
Object.keys(value) === null ||
(typeof value === 'string' && value.trim().length === 0)
);
};
exemple of my object :
offerData : {
valueOne : "hello",
objectOne : {
valueOne: 'Hello',
valueTwo : null}
}
problem : isEmpty work perfectly and check in my object if value, nested object or array are empty. But sometimes, nested object is not empty but have value "null".
I need to add in my "isEmpty" some condition to check each value in nested object and array.
So, in this situation, setCheckIfEmpty will return 'false' because my "objectOne" is not empty, even if "objectOne.valueTwo" === null.
I'm actually trying to map through each value, but for the moment it is not working.
To recursively check objects, you should modify your isEmpty function to call itself on nested objects:
export const isEmpty = (value) => {
if (typeof value === 'object' && value !== null) {
for (const v of Object.values(value)) {
if (isEmpty(v)) return true;
}
}
return (
value === undefined ||
value === null ||
(typeof value === 'object' && Object.keys(value).length === 0) ||
// Also these lines aren't needed because Object.keys() always returns an array
// Object.keys(value) === undefined ||
// Object.keys(value) === null ||
(typeof value === 'string' && value.trim().length === 0)
);
};
To search inside an object's values or in any children, however deep inside the children might be, you can use recursion:
// Return true if object has a null, undefined, empty string or empty object value or if any children (however deep inside the children might be) has these value:
function isEmpty(myObject) {
for(var key in myObject) {
if(myObject[key] == null || myObject[key] == undefined) return true;
if((typeof myObject[key] === 'object' && Object.keys(myObject[key]).length === 0)) return true;
if((typeof myObject[key] === 'string' && myObject[key].trim().length === 0)) return true;
if(myObject[key] instanceof Object) return isEmpty(myObject[key]);
}
return false;
}
Testing:
let o = {
valueOne : "hello",
objectOne : {
valueOne: 'Hello',
valueTwo : null}
}
isEmpty(o); //true

TypeError can not convert undefined or null to an object

I am trying to filter the data , using lodash function.
if (
!_.isEqual(toJS(this.originalName), toJS(this.nameData)) &&
this.marks.subject.length
) {
let NameKeys = _.filter(Object.keys(this.nameData), key =>
key.startsWith('Jo-')
)
}
So, Here I am getting this exception. I here Object.keys() is getting set to null.
How do I add check for this ? Or fix this error.
first you can check whether it's actual object or not
function isThisRealObject(x) {
return x && // not null, undefined, false, 0, or ''
typeof x !== 'number' &&
typeof x !== 'boolean' &&
typeof x !== 'string';
}
Then you can call this function from where you need to check this
function findKeysFromObject(x) {
return isThisRealObject(x) && Object.keys(x).length > 0;
}

Function that compares two objects and assigns old object prop value to new object prop value if the new value is empty or null?

I'm trying to ensure that any existing, non-null or empty values are not overwritten with empty or null values from an API call.
For example, assume that,
originalReference['data']['articleTitle'] = 'Something or other';
and
reference['data']['articleTitle'] = '';,
where reference is the object that came back from the API and originalReference is the object that existed before the API call (was loaded from MySQL database).
I want to ensure that this function cycles through these complex objects (both should always be of the same length and have the same property names), and reassigns the old value to the new object.
So, in the use case above, after the function is done, the articleTitle in the object reference will be:
reference['data']['articleTitle'] = 'Something or other';
Here is what I have so far:
if (referenceLength == originalReferenceLength) {
try {
for (var prop in reference) {
// First check for undefined or null
if (reference[prop] != undefined) {
if (reference[prop] != null) {
if (typeof reference[prop] == 'string' && reference[prop].trim() == '') {
// Assign original value to new object if new value is empty string
reference[prop] = originalReference[prop];
}
// Check if current prop in both objects is an object
if (typeof reference[prop] == 'object' && typeof originalReference[prop] == 'object') {
for (var property in reference[prop]) {
// Check for undefined or null value in original
if (originalReference[prop][property] != undefined) {
if (originalReference[prop][property] != null) {
if (reference[prop][property] == null || reference[prop][property] == '') {
// Assign old non-null value to new object if new value is empty or null
reference[prop][property] = originalReference[prop][property];
}
}
}
}
}
if (Array.isArray(reference[prop]) && typeof Array.isArray(originalReference[prop])) {
// Recurse if both are arrays
reference[prop].forEach((item, index) => vm.evaluateEmptyValues(item, originalReference[prop][index]));
}
} else {
if (originalReference[prop] != undefined) {
if (originalReference[prop] != null) {
// Assign original value to new object
reference[prop] = originalReference[prop];
}
}
}
} else {
if (originalReference[prop] != undefined) {
if (originalReference[prop] != null) {
// Assign original value to new object
reference[prop] = originalReference[prop];
}
}
}
}
} catch(err) {
console.log(err);
}
}
Don't think there's anything wrong with it but it could be a lot [DRYer][1] and more reusable.
Specially the iteration of a prop that is an object itself just screams recursion, so here's possible refactoring:
function mergeNewReference(origRef, newRef){
if(!newRef){
console.log('newRef is empty, not sure what should happen')
}else{
for(prop in newRef){
var newVal = newRef[prop];
if(!isNullOrUndefined(newVal)){
var origVal = origRef[prop];
if(typeof newVal == typeof origVal && typeof newVal === 'object'){
mergeNewReference(origVal, newVal)
}else if(isNullOrUndefined(origVal) || isEmptyString(origVal)){
origRef[prop] = newVal;
}
}
}
}
return origRef;
}
//These helper methods could be better encapsulated inside the merge method.
//I'll leave them here for better readability
function isNullOrUndefined(val){
return val === null || val === undefined;
}
function isEmptyString(str){
return typeof str == 'string' && str.trim() == ''
}
//----------- TEST ----------
var a = {a: null, b: {ba: 1, bb: {bba: '', bbb: null, bbx: 1, bbd: [9,null,9,9]}}};
var b = {a: 2, b: {ba: 2, bb: {bba: 'Not null', bbb: 2, bbc: 2, bbd: [1,2,3,4,5]}}};
var c = mergeNewReference(a,b);
console.log(c);
NOTICE: this will return a different result from you algorithm if you have nested objects with more than one level as it does a deep merge.
[1]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself

Javascript compare value in object

The deepEqual function is supposed to take in 2 values in see if they are exactly the same. The results for the first and second test come back as expected. Is there perhaps something wrong with the way I'm incorporating the recursive call?
function deepEqual(obj1, obj2) {
if (typeof obj1 == 'object' && typeof obj1 !== null && typeof obj2 == 'object' && typeof obj2 !== null) {
if (obj1.length != obj2.length) {return false;}
for (var prop in obj1) {
if (typeof obj1[prop] == 'object') {deepEqual(obj1[prop], obj2[prop]);}
if (obj1[prop] != obj2[prop]) {return false;}
}
return true;
} else if (obj1 === obj2) {return true;}
else {return false;}
}
var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
// → true -> true
console.log(deepEqual(obj, {here: 1, object: 2}));
// → false -> false
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
// → true -> false
Is there perhaps something wrong with the way I'm incorporating the recursive call?
Yes, you're throwing away its return value. You should be checking its return value and, if it's false, returning false early.
if (typeof obj1[prop] == 'object') {
if (!deepEqual(obj1[prop], obj2[prop])) {
return false;
}
}
Side note: There are several other issues in that code. I'm not going to get into a full review of it, but for instance, the if following the line above testing obj1[prop] to see if it's an object really should be else if, e.g.:
if (typeof obj1[prop] == 'object') {
if (!deepEqual(obj1[prop], obj2[prop])) {
return false;
}
} else if (obj1[prop] != obj2[prop]) {
//^^^^------------------------------------------ here
return false;
}

Pure JavaScript each function not working

In pure JavaScript, I am trying to make the jQuery.each function. So far I have just copied parts from the query source code.
Here is what I have so far:
var class2type = {
"[object Boolean]": "boolean",
"[object Number]": "number",
"[object String]": "string",
"[object Function]": "function",
"[object Array]": "array",
"[object Date]": "date",
"[object RegExp]": "regexp",
"[object Object]": "object",
"[object Error]": "error"
},
core_toString = class2type.toString;
function type(obj) {
if (obj == null) {
return String(obj);
}
return typeof obj === "object" || typeof obj === "function" ? class2type[core_toString.call(obj)] || "object" : typeof obj;
}
function isWindow(obj) {
return obj != null && obj == obj.window;
}
function isArraylike(obj) {
var length = obj.length,
type = type(obj);
if (isWindow(obj)) {
return false;
}
if (obj.nodeType === 1 && length) {
return true;
}
return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj);
}
function each( obj, callback, args ) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike( obj );
if ( args ) {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
}
} else {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
}
}
return obj;
}
It should work fine, but when I I try to run the following code:
each([1, 2], function( index, value ) {
alert( index + ": " + value );
});
I get the following error: TypeError: 'undefined' is not a function (evaluating 'type(obj)') This refers to here:
23| function isArraylike(obj) {
24| var length = obj.length,
25| type = type(obj);
Why won't this code work? I just used parts directly from jQuery's source code.
Thank you.
The problem is one of variable hoisting and shadowing. You have a type function outside of the current scope and you expect that in the statement on line 25 it is the one used as a function and then the result is passed to the local variable with the same name:
function type () {};
function isArraylike(){
var type = type(1);
};
In fact, what the code looks like due to variable hoisting is:
function type() {};
function isArraylike(){
var type; // type is undefined here
type = type(1);
};
So you can see that throughout the isArraylike function, type will always be a variable and it will never reference the function from the outer scope. The fix is simple: use another name either for the function or the variable.

Categories