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);
Related
when we need to compare two objects a and b we also should test that one of them is not null.
However, knowing that is a total chaos
{} - null => -0
[] - null => 0
1 - null => 1
"1" - null => 1
true - null => 1
false - null => 0
"a" - null => NaN
null - null => 0
"a" == null false
"a" > null false
"a" < null false
let arr = [
{ name: "a" },
{ name: null },
null,
{ name: "zaa" },
{ name: "dgh" }
];
let sortByName = function (a, b) {
if (a == null || b == null) return a - b;
if (a.name == null || b.name == null) return a.name - b.name;
return a.name.localeCompare(b.name);
};
console.log(arr.sort(sortByName));
the result is the following:
0: {name: 'a'}
1: {name: null}
2: null
3: {name: 'dgh'}
4: {name: 'zaa'}
how would you explain such a result?
null - {} === NaN
{} - null === -0
Here:
if (a == null || b == null) return a - b;
You are subtracting anything from null or null from anything, if one of the 2 values is null.
Replace null with an empty string and use that in your comparison:
let arr = [
{ name: "a" },
{ name: null },
null,
{ name: "zaa" },
{ name: "dgh" }
];
let sortByName = function (a, b) {
return (a?.name ?? '').localeCompare(b?.name ?? '');
};
console.log(arr.sort(sortByName));
If you want to make it so that null comes before {name: null} you can extend #Cerbrus answer to handle this.
A simple way to do this is convert both terms a and b into values that will in the end sort the way you like. I find the easiest way is to just prefix the input terms with another character you want to sort on.
eg.
If term is null, just return `0`;
If term is `{name:null}` return `1`;
Everything else just return `3` + term;
It's kind of like creating a compound index..
Doing the above you can see it's very easy to make the sort do whatever you want based on certain conditions.. Eg, if you wanted null to go to the end like undefined simply change it's prefix, eg. make it 4
eg.
let arr = [
{ name: "a" },
{ name: null },
null,
{ name: "zaa" },
{ name: "dgh" }
];
function toStr(a) {
if (a === null) return '0';
else if (a.name === null) return '1';
else return '3' + a.name;
}
let sortByName = function (a, b) {
return toStr(a).localeCompare(toStr(b));
};
console.log(arr.sort(sortByName));
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)
}
I have next array of objects:
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
What I need is to filter out elements which is empty, null or undefined and convert it to one object pameters:
{
givenName: 'firstName',
familyName: 'lastName',
'custom:data': 'blabla'
}
You could filter the array by looking to the values. This approach assumes, that only one key/value pair is available.
const
fields = [{ givenName: 'firstName' }, { familyName: 'lastName' }, { 'custom:data': 'blabla' }, { 'custom:data2': '' }, { 'custom:data3': null }],
result = Object.assign({}, ...fields.filter(o => {
const [v] = Object.values(o);
return v || v === 0 || v === false;
}));
console.log(result);
How to check whether a value is empty?
Most people would go about this with a truthy check:
const empty = x => x ? false : true;
empty(null); //=> true
empty(undefined); //=> true
empty(''); //=> true
But that's always going to exclude things you perhaps didn't intend to exclude:
empty(0); //=> true
empty(false); //=> true
empty(NaN); //=> true
Admittedly NaN could be seen as the "empty" value of its type but for the sake of your question and educational purpose we'll say it's not.
The "workaround" is often something like that:
const empty = x => (x || x === 0 || x === false || Number.isNaN(x)) ? false : true;
However this doesn't need to be more complicated than this:
const empty = x => x == null || x === '' ? true : false;
Checking for either undefined or null is one example where not using triple equality makes sense:
null == undefined;
// true
null === undefined;
// false
See Google JavaScript Style Guide.
If you need to exclude null, undefined and '' please don't rely on clever shorthand tricks and just be explicit about it. Type checking should be a straightforward job (YMMV) and not a show-off contest. Future you and your team mates will thank you.
As for your question, I'd suggest this:
Merge everything you've got with Object.assign:
Object.assign({}, {a:1}, {b:2}, {c:2});
// {a: 1, b: 2, c: 3}
Then deconstruct it into pairs, exclude those whose value is empty, then reconstruct the object from what's left:
const merge = xs =>
Object.fromEntries(
Object.entries(
Object.assign({}, ...xs))
.filter(([_, v]) =>
v != null && v !== ''));
console.log(merge(fields));
<script>
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
</script>
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
res = fields.reduce((acc, cur) => {
if (cur[Object.keys(cur)[0]]) {
acc = { ...acc, ...cur }
}
return acc
}, {})
console.log(res)
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
const result = fields.reduce( ( acc, field ) => {
Object.keys( field ).forEach( ( key ) => {
if( field[key] ) {
acc[key] = field[key];
}
} )
return acc;
}, {} )
console.log(result)
You could use reduce and forEach and check if value of each property is falsy or not.
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
const result = fields.reduce((r, e) => {
Object.entries(e).forEach(([k, v]) => {
if (v || [false, 0].includes(v)) r[k] = v
})
return r
}, {})
console.log(result)
Use Array.prototype.filter() method to filter out the empty, null or undefined objects. Then using Array.prototype.map() make a key-value pair array. At last, use Object.fromEntries() method to transform it to a single object.
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
const ret = Object.fromEntries(
fields
.filter((x) => {
const value = Object.values(x)[0];
return value || value === false || value === 0 || Object.is(value, NaN);
})
.map((x) => [Object.keys(x)[0], Object.values(x)[0]])
);
console.log(ret);
It might help you.
const fields = [
{ givenName: 'firstName' },
{ familyName: 'lastName' },
{ 'custom:data': 'blabla' },
{ 'custom:data2': '' },
{ 'custom:data3': null },
];
let item = {};
for ( let i = 0; i < fields.length; i++ ){
for (const [key, value] of Object.entries(fields[i])) {
if ( value !== null && value !== '' )
item [key] = value;
}
}
console.log(item);
Works with one key, simple modification can work on n keys.
const fields = [
{ givenName: "firstName" },
{ familyName: "lastName" },
{ "custom:data": "blabla" },
{ "custom:data2": "" },
{ "custom:data3": null },
];
const reduced = fields
.filter((f) => {
const key = Object.keys(f)[0];
return f[key] === "" || f[key] === null || f[key] === undefined
? false
: true;
})
.reduce((acc, curr) => {
const key = Object.keys(curr)[0];
acc[key] = curr[key];
return acc;
}, {});
console.log(reduced);
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)
I want to hide a button if there is atleast one order or subareas that at least one orders whose status 'ACCEPTED' or 'DONE'.
How can I hide the "Hide me" Menu item when either item has at least one area with order status 'ACCEPTED' OR 'DONE' or at least one area with subareas order with status 'ACCEPTED' or 'DONE'.
Below is the react code with the item I am trying to process
function Parent() {
const item = {
owner: {
id: '1',
cognitoId: '2',
},
areas: [{
id: '1',
orders: [{
id: '1',
status: 'ASSIGNED',
}, {
id: '2',
status: 'ACCEPTED',
}
],
subAreas: [{
id: '1',
orders: [{
id: '4',
status: 'DONE',
}
],
}
]
}, {
id: '2',
orders: [{
id: '3',
status: 'ASSIGNED',
}, {
id: '4',
status: 'ACCEPTED',
}
],
subAreas: [{
id: '2',
orders: [{
id: '5',
status: 'DONE',
}, {
id: '6',
status: 'ACCEPTED',
}
],
}
]
}
]
}
return ({
item && item.owner && item.owner.cognitoId && (
< Menu > Hide me < / Menu > )
});
}
EDIT
strong text
As per the solution provided i have tried like below,
const hasDoneAccepted = () => {
Object
.keys(item)
.some(key =>
(key === 'status' &&
['DONE', 'ACCEPTED'].includes(item[key])) || //error //here
(typeof item[key] == 'object' && //error here
hasDoneAccepted(item[key])) //error here
};
But this gives me error
Element implicitly has any type because expression of type "status" cant be used on index type 'Item'. property status doesnt exist on type 'Item'.
EDIT2strong text
based on solution for typescript implemented like below,
const hasDoneAccepted = (o: any) => {
const output = Object.keys(o).some(
key =>
(key === 'status' && ['DONE', 'ACCEPTED'].indexOf(o[key]) > -1) ||
(typeof o[key] === 'object' && hasDoneAccepted(o[key]))
);
return output;
};
console.log("output***", hasDoneAccepted(item));
this doesnt give any errors but in runtime the app breaks. and in the console i see the error.
"cannnot convert undefined or null to object"
You may traverse recursively (e.g. with Array.prototype.some()) your source object all deep through to find out, whether some key, named status has value either'DONE' or 'ACCEPTED' within nested properties:
const item = {owner:{id:'1',cognitoId:'2',},areas:[{id:'1',orders:[{id:'1',status:'ASSIGNED',},{id:'2',status:'ACCEPTED',}],subAreas:[{id:'1',orders:[{id:'4',status:'DONE',}],}]},{id:'2',orders:[{id:'3',status:'ASSIGNED',},{id:'4',status:'ACCEPTED',}],subAreas:[{id:'2',orders:[{id:'5',status:'DONE',},{id:'6',status:'ACCEPTED',}],}]}]},
hasDoneAccepted = o =>
Object
.keys(o)
.some(key =>
(key == 'status' &&
['DONE', 'ACCEPTED'].includes(o[key])) ||
(typeof o[key] == 'object' &&
hasDoneAccepted(o[key])))
console.log(hasDoneAccepted(item))
So, your conditional rendering may look something, like that:
function Parent() {
const hasDoneAccepted = ..
..
return !!item?.owner?.cognitoId && hasDoneAccepted(item) &&
(<Menu>Hide me</Menu>)
}