Remove empty string field from a object contain nested object and array? - javascript

I have ask another question, but someone close that question. I really need this answer. That's why I asking another question.
I have a object like following. I have to remove that empty string filed from nested object and also from nested array. How can I remove that.
const obj = {
name: 'Red Boy',
price: '350',
originalPrice: '', // Empty string field
stock: 20,
category: {
name: '', // Empty String field
subCategory: { name: ''} // Empty String filed
},
weight: '90kg',
dimensions: {
width: '50cm',
height: '', // Empty string filed
length: '70cm'
},
suitable: [
{ name: 'Yoga' },
{ name: '' }, // Empty String filed
{ name: 'Winter' }
],
additionalInfo: [
{ field: 'closure', value: 'Button' },
{ field: 'collar', value: ''} // Empty String Field
]
}
In this hybrid object type you can see some sub-object and also some sub-array. You can also see some field that are not contain any value.(I comment out that filed).
Actually I need to remove that filed. How can I remove that empty string field from above hybrid object type.
Thank you..
My Expected result-
{
name: 'Red Boy',
price: '350',
// Removed
stock: 20,
category: {
name: '', // Empty String field
// Removed
},
weight: '90kg',
dimensions: {
width: '50cm',
// Removed
length: '70cm'
},
suitable: [
{ name: 'Yoga' },
//Removed
{ name: 'Winter' }
],
additionalInfo: [
{ field: 'closure', value: 'Button' },
{ field: 'collar', //Removed }
// Here If this two filed is empty then should remove the whole object
{ field: '', value: '' }
// Then should remove whole '{ field: '', value: '' }'
]
}

To achieve this, we need to implement a recursive function to remove all empty string in all nested arrays and objects.
function rec(obj){
for(let key of Object.keys(obj)){
if (obj[key] === ''){
delete obj[key];
}
else if (typeof obj[key] === 'object'){
obj[key] = rec(obj[key]);
if (Object.keys(obj[key]).length === 0 ) delete obj[key];
}
}
return Array.isArray(obj) ? obj.filter(val => val) : obj;
}
Also, please note that it's not purely hybrid. Because Array is special type of Object.

const obj = {
name: 'Red Boy',
price: '350',
originalPrice: '', // Empty string field
stock: 20,
category: {
name: '', // Empty String field
subCategory: { name: ''} // Empty String filed
},
weight: '90kg',
dimensions: {
width: '50cm',
height: '', // Empty string filed
length: '70cm'
},
suitable: [
{ name: 'Yoga' },
{ name: '' }, // Empty String filed
{ name: 'Winter' }
],
additionalInfo: [
{ field: 'closure', value: 'Button' },
{ field: 'collar', value: ''} // Empty String Field
]
}
function removeEmptyString(object) {
Object
.entries(object)
.forEach(([key, value]) => {
if (value && typeof value === 'object')
removeEmptyString(value);
if (value &&
typeof value === 'object' &&
!Object.keys(value).length ||
value === null ||
value === undefined ||
value.length === 0
) {
if (Array.isArray(object))
object.splice(key, 1);
else
delete object[key];
}
});
return object;
}
console.log(removeEmptyString(obj))

I have used recursion to filter out the empty string, empty object or empty array present deep inside the nested structure.
This function also removes such objects and their nested objects with no properties.
Note: It will also work if the provided initial value is any other thing then object like array or string
var obj={name:"Red Boy",price:"350",originalPrice:"",stock:20,category:{name:"",subCategory:{name:""}},weight:"90kg",dimensions:{width:"50cm",height:"",length:"70cm"},suitable:[{name:"Yoga"},{name:""},{name:"Winter"}],additionalInfo:[{field:"closure",value:"Button"},{field:"collar",value:""}]};
function filt(a) {
if (typeof a === 'string') return a !== '';
//if it is a string, then it must not be empty
else if (Array.isArray(a)) return a.length !== 0
//if it an arra, then it must have some item
else if (a instanceof Object) return Object.keys(a).length !== 0;
//if it is an object, then it must have some property
return a !== null && a !== undefined
//else it must not be null or undefined
}
function rec(obj) {
if (Array.isArray(obj)) {
//if an value is an array
return obj.map((a) => rec(a)).filter((a) => filt(a)) //recurse the child first of each value in the array
//then filter out the value which are either null, empty, undefined or have length 0
} else if (obj instanceof Object) {
//if value is an object
var d = Object.entries(obj).map((a) => ([a[0], rec(a[1])])).filter((a) => filt(a[1]));
//map through the object.entries and reassign the values to the keys by recurssing over the value to filter out the nested inside irrelevant value
return Object.fromEntries(d)
//convert the map into object and return
} else if (typeof obj === 'string') return obj !== '' ? obj : null
//f it is a string, it must not be empty else return null
return obj !== null && obj !== undefined ? obj : null
//else it must not be null or undefined
}
console.log("For object",rec(obj))
console.log("For Array",rec([{
name: "Yoga"
}, {
name: ""
}, {
name: "Winter"
}]))

Here's an immutable way to remove non-empty values from an object, with the added capability of normalising object's inside of arrays:
const normaliseObject = (obj) =>
Object.fromEntries(
Object.entries(obj)
.filter(([_, value]) => value !== '' && value !== null)
.map(([key, value]) => (typeof value === 'object' ? [key, normalise(value)] : [key, value])),
)
// If an element in an array is an object, normalise that too
const normaliseArray = (arr) => arr.map(value => (typeof value === 'object' ? normalise(value) : value))
/**
* Normalise any object to remove keys whose values are falsey
* #param obj Object to be normalised
* #returns Normalised object where falsey values are removed
*/
const normalise = (obj) => {
return Array.isArray(obj) ? normaliseArray(obj) : normaliseObject(obj)
}

Related

(JavaScript) Condition in the replacer parameter in JSON

Why this gives me the properties:
const person = {
id: 1,
firstName: 'Jack',
lastName: 'White',
age: 25,
};
const json = JSON.stringify(person, (key, value) => typeof(value) == "string" ? undefined : value);
console.log(json);
But this gives me 'undefined':
const person = {
id: 1,
firstName: 'Jack',
lastName: 'White',
age: 25,
};
const json = JSON.stringify(person, (key, value) => typeof(value) == "number" ? value : undefined);
console.log(json);
Can't get the differences. Is there a syntax error or just can't works in this way?
Because the entire object is evaluated first, and then each key/value pair. You are denying the object first, because it is not a number, so the whole thing is undefined. If you match objects and numbers, it works.
const person = {
id: 1,
firstName: 'Jack',
lastName: 'White',
age: 25,
};
const json = JSON.stringify(person, (key, value) => (typeof(value) == "number" || typeof(value) == "object") ? value : undefined);
console.log(json);
This means:
typeof(value) == "string" ? undefined : value
When a string is found, return undefined. Otherwise, return the original value. So, strings get removed.
This means:
typeof(value) == "number" ? value : undefined
When a number is found, return that number. Otherwise, return undefined. So, non-numbers get removed. The only value that would produce a result would be a plain number.
const json = JSON.stringify(3, (key, value) => typeof(value) == "number" ? value : undefined);
console.log(json);
Perhaps you meant to do
typeof value == "number" ? undefined : value
removing numbers, but keeping everything else as is.
const person = {
id: 1,
firstName: 'Jack',
lastName: 'White',
age: 25,
};
const json = JSON.stringify(person, (key, value) => typeof(value) == "number" ? undefined : value);
console.log(json);
Or perhaps you meant to remove only non-number primitives.
typeof value === "object" || typeof value === 'number' ? value : undefined
const person = {
id: 1,
firstName: 'Jack',
lastName: 'White',
age: 25,
};
const json = JSON.stringify(person, (key, value) => typeof value === "object" || typeof value === 'number' ? value : undefined);
console.log(json);

a better way to loop through an array of items

I have a model that looks as follows
this.Model
Model {class: undefined items: Array(0) tag: undefined launch: undefined length: undefined name: undefined Id: "de4d704a-b754-4546-b3ab-f0c131eba84a" time: "15:36" tonnage: undefined}
the only objects in the Model that will always have a value is Id and Time.
i have an if statement that goes through each off my objects to check if its null as follows:
if ( this.Model.class == null && this.Model.name == null && this.Model.tag== null && this.Model.launch == null && this.Model.length == null && this.Model.tonnage == null && this.Model.items.length == 0)
{
//does something in here
}
so i want to check if all the objects are null except time and ID, is there a better way of doing this than me using the above method in an if statement?
I would create a function to check about that using Object.entries and Array.every.
Perks of this solution :
Reusable utility function.
Works with any number of keys to ignore.
The typing of the function will throw an error in case you specify a key to ignore that is not a part of the provided object.
Playground in TypeScript
Snippet in Javascript.
function checkAllKeysExceptGivenKeysToBeNullable(obj, keys) {
return Object.entries(obj).every(([
key,
value,
]) => {
// If the key has to be ignored
if (keys.includes(key)) {
return true;
}
// Check the value to be nullable
return value === null ||
value === void 0 ||
(value instanceof Array && value.length === 0);
});
}
console.log(checkAllKeysExceptGivenKeysToBeNullable({
class: undefined,
items: Array(0),
tag: undefined,
launch: undefined,
length: undefined,
name: undefined,
Id: 'de4d704a-b754-4546-b3ab-f0c131eba84a',
time: '15:36',
tonnage: undefined,
}, [
'Id',
'time',
]));
console.log(checkAllKeysExceptGivenKeysToBeNullable({
class: undefined,
items: Array(0),
tag: 'nope',
launch: undefined,
length: undefined,
name: undefined,
Id: 'de4d704a-b754-4546-b3ab-f0c131eba84a',
time: '15:36',
tonnage: undefined,
}, [
'Id',
'time',
]));
function checkAllKeysExceptGivenKeysToBeNullable<T extends {
[key in keyof T]: null | undefined | Array<unknown> | unknown;
}>(obj: T, keys: (keyof T)[]): boolean {
return Object.entries(obj).every(([
key,
value,
]) => {
if (keys.includes(key as keyof T)) {
return true;
}
return value === null ||
value === void 0 ||
(value instanceof Array && value.length === 0);
});
}
console.log(checkAllKeysExceptGivenKeysToBeNullable({
class: undefined,
items: Array(0),
tag: undefined,
launch: undefined,
length: undefined,
name: undefined,
Id: 'de4d704a-b754-4546-b3ab-f0c131eba84a',
time: '15:36',
tonnage: undefined,
}, [
'Id',
'time',
]));
console.log(checkAllKeysExceptGivenKeysToBeNullable({
class: undefined,
items: Array(0),
tag: 'nope',
launch: undefined,
length: undefined,
name: undefined,
Id: 'de4d704a-b754-4546-b3ab-f0c131eba84a',
time: '15:36',
tonnage: undefined,
}, [
'Id',
'time',
]));
After playing around a bit, I came up with a solution using Object.keys()
/**
* Checks if all of the objects values are null, except for keys in param except
* #param obj: The object to test
* #param except (optional): keys to omit the null check on
*/
function checkIfPropertiesNull(obj: {[key: string]: unknown}, except: string[] = []): boolean {
const keys = Object.keys(obj).filter(key => !except.includes(key));
for(const key of keys){
if(obj[key] !== null){
return false;
}
}
return true;
}
console.log(checkIfPropertiesNull({ id: 1, name: 'mike', city: null }, ['id', 'name'])); // true, because id and name are not checked
console.log(checkIfPropertiesNull({ id: 1, name: 'mike', city: 'Munich' }, ['id', 'name'])); // false, city isn't null
Playground
for (let prop in Modal) {
if (Modal.prop != time && Modal.prop != id && Modal[prop] != null) {
return false
}
}
return true
Return true if all properties except ID and Time are null.
Well, you have two options in this case:
Put your loop in a function to separate this logic to your model:
function checkProperties(obj) {
for (var key in obj) {
if (obj[key] !== null && obj[key] != "")
return false;
}
return true;
}
var obj = {
x: null,
y: "",
z: 1
}
checkProperties(obj) //returns false
Use Object.values and every to check your properties as an array.
let report = {
property1: null,
property2: null,
}
let result = !Object.values(report).every(o => o === null);
console.log(result);

Assign value of object and nested object in JavaScript

So, basically, I have this object (in vue data()):
...
detail: {
title: 'I have no idea',
date: 2020-02-12,
sender: {
name: 'John Cenna',
username: 'john10'
},
receipent: {
name: 'Katleen',
username: 'katmeow'
},
....
}
Now, I want to assign all of them to be null (for validation process, kind of) but couldn't assign the nested object (the object without child is fine with just loop). How do I do this? thank you
A recursive solution:
Use Object.entries() to iterate the key-value entries of the object as follows. Note that Object.entries() returns an empty array for non-objects.
If the entry's value is already null or undefined, return.
If the entry's value is an array, recurse on each array item (go to step 1 with the array item as the new object to evaluate).
If the entry's value is an object, recurse on the object.
Otherwise, set the entry's value to null.
const data = {
detail: {
title: 'I have no idea',
date: 2020 - 02 - 12,
sender: {
name: 'John Cenna',
username: 'john10'
},
receipent: {
name: 'Katleen',
username: 'katmeow'
},
items: [
{ id: 100, name: 'Apple' },
{ id: 200, name: 'Banana' },
{ id: 300, name: 'Orange' },
]
}
}
function nullify(obj) {
// 1
Object.entries(obj).forEach(([k,v]) => {
// 2
if (v === null || v === undefined) {
return
}
// 3
if (Array.isArray(v)) {
return v.forEach(nullify)
}
// 4
if (typeof v === 'object') {
nullify(obj[k])
} else {
// 5
obj[k] = null
}
})
}
nullify(data.detail)
console.log('res', data.detail)

Angular custom Filter pipe after KeyValue pipe

I'm trying to implement a custom filter after using Angular's inbuilt Key Value pipe
I have an array with values for example
object= [
{ key: "Added_By", value: "Yi" },
{ key: "Assigned_To", value: "-" },
{ key: "Bought_Date", value: 43810 },
{ key: "Brand", value: "Samsung PM863" },
{ key: "Capacity", value: 3.84 },
]
I want to filter based on multiple incoming values but incoming values vary for example
It Could be 1 key/Value
Filter= [{ key: "Added_By", value: "Yi" }]// Return Object
or multiple
Filter= [{ key: "Added_By", value: "Yi" }, { key: "Bought_Date", value: 43810 }] //both matches return object
Filter= [{ key: "Added_By", value: "ABC" }, { key: "Bought_Date", value: 43810 }] //1 match but 1 doesn't return false
I want to return object if all the conditions are met
For a single key/value I tried
let Key= Filter[0].key
let Value=Filter[0].value
let KeyFilter = object.filter(x => x.key === Key)
if (KeyFilter[0].value.toString().toLowerCase().indexOf(Value.toLowerCase()) !== -1)
return items
But this approach only works only when 1 object is present in filter array
I've created a function that accepts an array of objects, and an object with key: value pairs.
The keys of objects in the array must match with object keys to work.
I always use it when I need to filter an array based on various conditions.
export const filterArray = (filterData, toBeFiltered) => {
return toBeFiltered.filter(item => {
for (const key in filterData) {
if (item[key] === null || item[key] === undefined) {
return false;
} else {
if (typeof filterData[key] === 'string') {
if (!(item[key]).toLowerCase().includes((filterData[key].trim()).toLowerCase())) {
return false;
}
} else if (item[key] !== filterData[key]) {
return false;
}
}
}
return true;
});
};
Hope it helps.

Find a value in an object containing nested objects and array

I have the following object and a value -
{
location:"xyz",
title:"abc",
company: {
address:"address can have spaces",
name:"name"
},
array-key :[
{ skill : "skill1"},
{ skill : "skill2"},
{ skill : "skill3"}
],
description :"brief description"
}
and now I have a value - "spaces", now I want to check if "spaces" is present in the object at any level. If "spaces" is present function should return true.
I tried the recursive way but how should I handle the array?
One useful trick for iterating recursively over an object is to use the replacer parameter to JSON.stringify.
function findString(obj, regexp) {
let found = false;
JSON.stringify(obj, (k, v) => {
if (found || typeof v === 'string' && regexp.test(v)) found = true;
else return v;
});
return found;
}
Just for completeness with iterating all levels recursively and checking the value either strict or as string and with String#indexOf.
function check(object, value) {
return Object.keys(object).some(function (key) {
if (object[key] && typeof object[key] === 'object') {
return check(object[key], value);
}
return object[key] === value || object[key].toString().indexOf(value) + 1;
});
}
var data = { location: "xyz", title: "abc", company: { address: "address can have spaces", name: "name" }, arrayKey: [{ skill: "skill1" }, { skill: "skill2" }, { skill: "skill3" }], description: "brief description" };
console.log(check(data, "spaces"));
console.log(check(data, "foo"));

Categories