I am trying to come up with a solution in vanilla JS (without using any third party libraries) that checks a given input and determines whether it is 'empty' or not.
I have the following code & assertions that I'd like to match against. Each test case has an expected answer as a comment.
I have tried several functions to 'deepCheck' these assertions on stackblitz but none have come close to getting full coverage.
https://stackblitz.com/edit/node-ksxnjm
const assert = require('assert');
function isEmpty(obj) {
return Object.keys(obj).every((k) => !Object.keys(obj[k]).length);
}
const test1 = {}; // expect true
const test2 = { some: 'value' }; // expect false
const test3 = { some: {} }; // expect true
const test4 = []; // expect true
const test5 = [[]]; // expect true
const test6 = { some: [] }; // expect true
const test7 = { some: ['barry'] }; // expect false
const test8 = { some: new Map() }; // expect true
const test9 = {
response: new Map([['body', new Map([['something', {}]])]]),
}; // expect true
const test10 = {
response: '{"body":{"something":{}}}',
}; // expect true
const test11 = {
something: { somethingElse: {} },
}; // expect true
assert.strictEqual(isEmpty(test1), true);
assert.strictEqual(isEmpty(test2), false);
assert.strictEqual(isEmpty(test3), true);
assert.strictEqual(isEmpty(test4), true);
assert.strictEqual(isEmpty(test5), true);
assert.strictEqual(isEmpty(test6), true);
assert.strictEqual(isEmpty(test7), false);
assert.strictEqual(isEmpty(test8), true);
assert.strictEqual(isEmpty(test9), true);
assert.strictEqual(isEmpty(test10), true);
assert.strictEqual(isEmpty(test11), true);
The function I created works for the majority of these test cases, but not for all. The one's I'm struggling to cover are the nested objects and strignified objects. I'm a bit stumped as to how I can proceed.
How can I check for these latter test cases?
EDIT:
const test12 = {
something: {
somethingElse: {
number: 1,
someSet: new Set(['garry']),
},
},
}; // should evaluate to false
const test13 = new Map([
['something', new Map([
['somethingElse', new Map([
['number', 1],
['someSet', new Set(['garry'])]
])]
])]
]); // should also evaluate to false
CodePudding user response:
You can use a recursive function like this:
- Check if the value passed is an object
- If the object has a
values
function, then get the values of the object usingArray.from(o.values())
(Gets the values ofSet
,Map
andArray
objects) - Recursively call the isEmpty on every value
- If the input value is a string, then check if it is a truthy vaue (You can customize this part based on your need)
function isEmpty(o) {
if (typeof o === "object") {
let values = o.values === "function"
? Array.from(o.values())
: Object.values(o)
return values.every(isEmpty)
} else {
var parsed = parseJsonString(o)
return parsed ? isEmpty(parsed) : !o
}
};
function parseJsonString(str) {
try {
return JSON.parse(str);
} catch (e) {
return '';
}
return true;
}
Here's a runnable snippet:
function isEmpty(o) {
if (typeof o === "object") {
let values = o.values === "function"
? Array.from(o.values())
: Object.values(o)
return values.every(isEmpty)
} else {
var parsed = parseJsonString(o)
return parsed ? isEmpty(parsed) : !o
}
};
function parseJsonString(str) {
try {
return JSON.parse(str);
} catch (e) {
return '';
}
}
const test1 = {}; // expect true
const test2 = { some: 'value' }; // expect false
const test3 = { some: {} }; // expect true
const test4 = []; // expect true
const test5 = [[]]; // expect true
const test6 = { some: [] }; // expect true
const test7 = { some: ['barry'] }; // expect false
const test8 = { some: new Map() }; // expect true
const test9 = {
response: new Map([['body', new Map([['something', {}]])]]),
}; // expect true
const test10 = {
response: '{"body":{"something":{}}}',
}; // expect true
const test11 = {
something: { somethingElse: {} },
};
console.log(
[test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test11].map(isEmpty)
);
CodePudding user response:
I think that only way how to solve this is to use Recursion
You first have to decido on which data you currently working with and than look if the data have anything inside. If they do you have to call the function isEmpty again to check for the type of inside data and also if they have someting inside.
const isEmpty = value =>{
if(typeof value === 'object'){
if(Object.keys(value).length === 0) return true
for (const property in value) {
isEmpty(property)
}
}
else if(typeof value === 'string'){
//check for string
}
//...
//...
//...
//another types
}