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);
}
}
}
Related
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
I got a simple question, been hours on it and can't figuring out the solution. I got an array of object. I want to remove the object that has same values that a variable I pass.
My problem is that it removes all the objects, even those that are not similar to my variable.
Here is my code
function appSelect(variable) {
//we check if the object already exists in my array {id:x, value:y}
const found = myArray.find(obj => {
return (obj.id === variable.id && obj.value === variable.value);
});
if (found) {
//If find it, I want to remove it from my array
const filtered = myArray.filter(obj => {
return (obj.id !== variable.id && obj.value !== variable.value);
})
//Return empty array
}
I receive the value from a select form. For exemple I got myArray = [{id: 1, value: 12},{id: 2, value: 12},{id: 5, value: 12}] and variable = {id: 2, value: 12}
What I did wrong?
The reason is the code below:
return (obj.id !== variable.id && obj.value !== variable.value)
Which means if id or value is the same,then it will be filtered.
You can change it to
return !(obj.id === variable.id && obj.value === variable.value)
Full code:
function appSelect(variable) {
//we check if the object already exists in my array {id:x, value:y}
const found = myArray.find(obj => {
return (obj.id === variable.id && obj.value === variable.value);
});
if (found) {
//If find it, I want to remove it from my array
const filtered = myArray.filter(obj => {
return !(obj.id === variable.id && obj.value === variable.value);
})
//Return empty array
}
This is a failed DeMorgan's Law. You want the opposite of obj.id === variable.id && obj.value === variable.value, which is !(obj.id === variable.id && obj.value === variable.value) or obj.id !== variable.id || obj.value !== variable.value when DeMorgan's Law is applied.
const filtered = myArray.filter((obj) => {
return obj.id !== variable.id || obj.value !== variable.value;
});
if one of the value is different then it's not the same objet, note that the first found check is unnecessary
function appSelect(variable, arr) {
return arr.filter(obj => (obj.id !== variable.id || obj.value !== variable.value))
}
You can try this code:
const myArray = [
{ id: 1, value: 12 },
{ id: 2, value: 12 },
{ id: 5, value: 12 },
];
const variable = { id: 2, value: 12 };
function appSelect(variable) {
//we check if the object already exists in my array {id:x, value:y}
const found = myArray.find((obj) => {
return obj.id === variable.id && obj.value === variable.value;
});
if (found) {
//If find it, I want to remove it from my array
const filtered = myArray.filter((obj) => {
// if you found the object variable in your array then you return nothing
if (obj.id === variable.id && obj.value === variable.value) {
return;
}
// if the current object is not object variable then return it
return obj;
});
return filtered;
}
//Return empty array
return myArray;
}
console.log(appSelect(variable));
I have a problem with this structure:
const ob = {
name: ''
ob: {}
arr: []
}
I want to check if all values, are empty.
If I have only strings and arrays, the problem is trivial, but with an object my best solution is something like that,
const test = Object.values(ob).reduce((acc, curr) => {
const isPlainObject = typeof curr === 'object' && !Array.isArray(curr);
if (isPlainObject) !Object.values(curr).length ? (acc = false) : null;
else !curr.length ? (acc = false) : null;
return acc;
}, true);
I'm not satisfied with this, did anybody face similar problem and can help me with that?
You could check the various types and then either the length or the count of (own enumerable) keys or the value.
const
isEmpty = object => Object.values(object).every(v => {
if (Array.isArray(v)) return v.length === 0;
if (v && typeof v === 'object') return Object.keys(v).length === 0;
return v === '';
}),
ob = { name: '', ob: {}, arr: [] };
console.log(isEmpty(ob));
Maybe not so much better than your solution, C233. But I'll give it a shot ;)
const isEmpty = (val) => {
if(!val) {
return true;
}
if (typeof val === 'object') {
return Object.values(val).length === 0;
}
if (typeof val === 'string') {
return val.length === 0;
}
return false;
}
const hasEmptyProps = (obj) => Object.values.map(isEmpty).reduce((result, curr) => result && curr, true);
This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 4 years ago.
how can I deep clone an object, what could be wrong with this solution.
I wrote this decision, but I'm not sure if this is good, and what bottlenecks it has.
How to do it correctly on vanilla js, without using jQuery. If the object has (enumerable: false)?
let user = {
name: 'SomeName',sayHi: function(){console.log(this.name);}}
Object.defineProperty(user, 'sayHi', {enumerable:false});
function deepCloneNew(obj){
if (!obj) { return };
let cloneObj = {};
let keys = Object.getOwnPropertyNames(obj);
keys.forEach((key)=>{
if(typeof obj[key] === 'object' && obj[key] !== null){
deepCloneNew(obj[key]);
}
if(typeof obj[key] === 'function'){
Object.defineProperty(cloneObj, key, Object.getOwnPropertyDescriptor(obj, key));
}
if(typeof obj[key] !== 'object' && typeof obj[key] !== 'function' || obj[key] === null){
Object.defineProperty(cloneObj, key, Object.getOwnPropertyDescriptor(obj, key));
}
})
return cloneObj;
}
let copy = deepCloneNew(user);
Please Follow this
function clone(item) {
if (!item) { return item; } // null, undefined values check
var types = [ Number, String, Boolean ],
result;
// normalizing primitives if someone did new String('aaa'), or new Number('444');
types.forEach(function(type) {
if (item instanceof type) {
result = type( item );
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call( item ) === "[object Array]") {
result = [];
item.forEach(function(child, index, array) {
result[index] = clone( child );
});
} else if (typeof item == "object") {
// testing that this is DOM
if (item.nodeType && typeof item.cloneNode == "function") {
var result = item.cloneNode( true );
} else if (!item.prototype) { // check that this is a literal
if (item instanceof Date) {
result = new Date(item);
} else {
// it is an object literal
result = {};
for (var i in item) {
result[i] = clone( item[i] );
}
}
} else {
// depending what you would like here,
// just keep the reference, or create new object
if (false && item.constructor) {
// would not advice to do that, reason? Read below
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}
I want use extend a javascript object, upto all possible nested levels.
I am using this function (the returned function) as iterator
var ObjWalk = (function( GLOBAL_APP_CONFIG,GLOBAL_METHODS){
if (typeof GLOBAL_APP_CONFIG !== 'object' || GLOBAL_APP_CONFIG === null) GLOBAL_APP_CONFIG = {};
const maxobjdepth = GLOBAL_APP_CONFIG.maxobjdepth || 99;
const endvar = GLOBAL_APP_CONFIG.walkendkey || '$W_END';
let ifEndForObjWalk = GLOBAL_METHODS && GLOBAL_METHODS.ifEndForObjWalk;
if(typeof ifEndForObjWalk !== 'function') {
ifEndForObjWalk = function(obj, depth) {
return ((depth < maxobjdepth && typeof obj === 'object'
&& obj !== null && obj[endvar] !== true) ? obj : false);
};
};
const walkInto = function(fun, rt, obj, key, depth, isLast) {
if(!depth) depth = 0;
fun(obj, key, rt, depth || 0, typeof isLast === 'boolean' ? isLast : true);
const ob = ifEndForObjWalk(obj, depth);
if (ob) {
const kys = Object.keys(ob);
const lastln = kys.length - 1;
const deep = depth + 1;
for (let z = 0; z <= lastln; z += 1) {
walkInto(fun, ob, ob[kys[z]], kys[z], deep, (z === lastln));
}
}
};
return walkInto;
})();
The iterators simply iterates each and every possible values and call that value via a function, that is passed at calling.
I know there may be some other solutions available for deep extending javascript object, but i want to achieve deep extend via using this iterator only.
This is what i have done
const deepExtend = function(obj1, obj2) {
let ptr1 = obj1;
// resolve if new value should be array or object or no change
function resolve(ab, vl){
if ((typeof vl === 'object')
&& (ab === undefined || typeof ab !== 'object'
|| (vl === null && ab !== null)
|| (vl !== null && ab === null)
|| (Array.isArray(vl) && !Array.isArray(ab))
|| (!Array.isArray(vl) && Array.isArray(ab))
|| (vl.length > ab.length)
)
) {
ab = vl === null ? vl : (Array.isArray(vl) ? new Array(vl.length) : {});
}
return ab;
}
ptr1 = resolve(obj1, obj2);
if (typeof ptr1 !== 'object') return ptr1;
// ptrs1 = Array of pointers that will make the changes to a particular depth at a time.
const ptrs1 = [[null,true],[ptr1,true]];
// below vl = value, ky = key, ob = someOb where someOb[ky] = vl
// dpt = nested depth
// isLast = if the `ky` is the last key of someOb
ObjWalk(function(vl, ky, ob, dpt, isLast){
if (ky && dpt) {
// whenever a object found
if (typeof vl === 'object') {
ptr1[ky] = resolve(ptr1[ky], vl);
if(ptr1[ky] !== null) {
if (ptrs1[dpt+1]) {
// update if pointer already exists
ptrs1[dpt+1] = [ptr1[ky], isLast];
} else {
// or push a new pointer
ptrs1.push([ptr1[ky], isLast]);
}
ptr1 = ptr1[ky];
}
} else {
ptr1[ky] = vl;
if (isLast) {
let ind = dpt;
let cr = ptrs1[dpt];
// find the closest sub pointer that should change next
do {
ind--;
cr = ptrs1[ind];
ptr1 = cr[0];
} while (cr[1] !== true && ind);
}
}
}
}, null, obj2);
return obj1;
}
Sandbox for the one failure is here
https://jsfiddle.net/rfn3ejy0/5/
Its difficult to debug where is the issue.
Any help there?