How to check if value is empty in nested object - javascript

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

Related

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

Find a key in nested object and replace its value - Javascript

Here is my fiddle : DEMO
By recursive iterations, I am able to find the key in object2 object3 and replace its value by the values from data2 data3 objects.
However, I am unable to replace the value if it is an array. (key called 'coordinates' in this case)
How could this be fixed?
function update(object, data) {
function getAllKeys(o) {
Object.keys(o).forEach(function(k) {
if (typeof o[k] === 'object') {
return getAllKeys(o[k]);
}
keys[k] = o;
});
}
var keys = Object.create(null);
getAllKeys(object);
Object.keys(data).forEach(function(k) {
if (keys[k] && k in keys[k]) { // check if key for update exist
keys[k][k] = data[k];
}
});
}
Update the getAllKeys method with:
function getAllKeys(o) {
Object.keys(o).forEach(function(k) {
contains_object = Array.isArray(o[k]) && o[k].some(val=> { return typeof val == "object" && !Array.isArray(val); });
if ((Array.isArray(o[k]) && !contains_object) || typeof o[k] !== 'object') {
keys[k] = o;
} else {
return getAllKeys(o[k]);
}
keys[k] = o;
});
}
Note: !(o[k] instanceof Array) - http://jsfiddle.net/08pnu7rx/1/
The problem is that typeof also returns object for arrays.
You want to change your function to still assign the key when the object is an array.
function getAllKeys(o) {
Object.keys(o).forEach(function(k) {
if (Array.isArray(o[k]) || typeof o[k] !== 'object') {
keys[k] = o;
} else {
return getAllKeys(o[k]);
}
});
}
Notice I swapped around the logic, so you first check for either an array or another non-object type. If that check passes, you assign the value. If not, you recurse.
You should note that this is not at all specific to arrays. You will have similar problems if you have a nested property that is a Date, for example.
The problem in your code is that typeof o[k] === 'object' returns true even it o[k] is an array which is the case for coordinated, You need a negative check for array too like
Object.keys(o).forEach(function(k) {
if (typeof o[k] === 'object'&& !Array.isArray(o[k])) {
return getAllKeys(o[k]);
}
keys[k] = o;
});
Working fiddle
According to the docs or typeof:
// use Array.isArray or Object.prototype.toString.call
// to differentiate regular objects from arrays
typeof [1, 2, 4] === 'object';

Converting lodash's `_.pull` to vanilla JS? [duplicate]

This question already has answers here:
Remove empty elements from an array in Javascript
(49 answers)
Closed 5 years ago.
I'm having an issue with this function that recursively removes empty values from an object:
const _ = require('lodash')
function sanitize(object) {
Object.entries(object).forEach(([key, val]) => {
if (
val == null ||
Number.isNaN(val) ||
(typeof val === 'string' && isOnlyWhitespace(val)) ||
(typeof val === 'object' && Object.keys(sanitize(val)).length === 0)
) {
delete object[key]
}
});
// Remove `undefined` values leftover from using `delete` on an array.
if (Array.isArray(object)) {
_.pull(object, undefined); // THIS IS THE LINE IM TRYING TO CHANGE
}
return object;
}
function isOnlyWhitespace(str) {
return !(/\S/).test(str.trim());
}
I'm trying to replace _.pull(object, undefined) with vanilla JS, but nothing seems to give the right output (I've tried using stuff like filter.)
Here is a snippet you can run to see both outputs:
// LODASH VERSION
function lodashSanitize(object) {
Object.entries(object).forEach(([key, val]) => {
if (
val == null ||
Number.isNaN(val) ||
(typeof val === 'string' && isOnlyWhitespace(val)) ||
(typeof val === 'object' && Object.keys(lodashSanitize(val)).length === 0)
) {
delete object[key]
}
});
// Remove `undefined` values leftover from using `delete` on an array.
if (Array.isArray(object)) {
_.pull(object, undefined); // THIS IS THE LINE IM TRYING TO CHANGE
}
return object;
}
// MY VERSION
function mySanitize(object) {
Object.entries(object).forEach(([key, val]) => {
if (
val == null ||
Number.isNaN(val) ||
(typeof val === 'string' && isOnlyWhitespace(val)) ||
(typeof val === 'object' && Object.keys(mySanitize(val)).length === 0)
) {
delete object[key]
}
});
// Remove `undefined` values leftover from using `delete` on an array.
if (Array.isArray(object)) {
object = object.filter(val => val != null) // THIS IS MY ATTEMPT
}
return object;
}
function isOnlyWhitespace(str) {
return !(/\S/).test(str.trim());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<button id="lodash">Show lodash output</button>
<button id="me">Show my output</button>
<p id="output" />
<script>
/**
* Fiddle-related code, you can ignore this
*/
const lodashBtn = document.querySelector('#lodash')
const meBtn = document.querySelector('#me')
const output = document.querySelector('#output')
function createExampleInput() {
const input = {
name: 'John',
grades: [
90,
undefined,
50,
null
]
};
return input;
}
lodashBtn.addEventListener('click', () => {
output.textContent = JSON.stringify(lodashSanitize(createExampleInput()), null, 4)
});
meBtn.addEventListener('click', () => {
output.textContent = JSON.stringify(mySanitize(createExampleInput()), null, 4)
});
</script>
The problem is that filter returns a new array. Why not just use a for loop and splice:
if (Array.isArray(object)) {
for (var i = object.length - 1; i >= 0; i--) {
if (object[i] === undefined) {
object.splice(i, 1);
}
}
}

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

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>

Categories