I have a tree object as below, I am trying to remove the items array property if it's empty. I am not sure on the best approach to do this?
I am thinking of looping through the key, check the property and then remove using delete myJSONObject[prop]
... Any thoughts / ideas are wele?
[{
text: "TreeRoot",
items: [{
text: "Subgroup1",
items: []
}, {
text: "Subgroup2",
items: []
}, {
text: "Subgroup3",
items: [],
items: [{
text: "subgroup5",
items: [{
text: "subgroup6",
items: [{
text: "subgroup7",
items: [{
text: "subgroup8",
items: []
}]
}]
}]
}]
}]
}]
I have a tree object as below, I am trying to remove the items array property if it's empty. I am not sure on the best approach to do this?
I am thinking of looping through the key, check the property and then remove using delete myJSONObject[prop]
... Any thoughts / ideas are wele?
[{
text: "TreeRoot",
items: [{
text: "Subgroup1",
items: []
}, {
text: "Subgroup2",
items: []
}, {
text: "Subgroup3",
items: [],
items: [{
text: "subgroup5",
items: [{
text: "subgroup6",
items: [{
text: "subgroup7",
items: [{
text: "subgroup8",
items: []
}]
}]
}]
}]
}]
}]
Share
Improve this question
edited Jan 15, 2013 at 18:37
Learner
asked Jan 15, 2013 at 18:26
LearnerLearner
2,33910 gold badges46 silver badges83 bronze badges
3
- I would build a new object, rather than trying to modify this one. – Alex Wayne Commented Jan 15, 2013 at 18:39
- Hey, you solved this yet? – Gideon Commented Jul 8, 2015 at 9:02
- stackoverflow./questions/9446426/… – J4S0Nc Commented Aug 29, 2015 at 10:42
5 Answers
Reset to default 5This should do the job (ES5):
function removeEmpty(obj) {
Object.keys(obj).forEach(function(key) {
(key === 'items' && obj[key].length === 0) && delete obj[key] ||
(obj[key] && typeof obj[key] === 'object') && removeEmpty(obj[key])
});
return obj;
};
JSBIN
Same in ES6:
const removeEmpty = (obj) => {
Object.keys(obj).forEach(key =>
(key === 'items' && obj[key].length === 0) && delete obj[key] ||
(obj[key] && typeof obj[key] === 'object') && removeEmpty(obj[key])
);
return obj;
};
JSBIN
You can have recursive algorithm that at each step either removes items
array and returns, or recursively processes each individual object of the array.
I would also try to do this on the server-side. It will save a lot of plexity, memory, and processing time. There are usually ways of "excluding" empty arrays from the JSON encoded string.
Here is a solution using object-scan
// const objectScan = require('object-scan');
const data = [{ text: 'TreeRoot', items: [{ text: 'Subgroup1', items: [] }, { text: 'Subgroup2', items: [] }, { text: 'Subgroup3', items: [{ text: 'subgroup5', items: [{ text: 'subgroup6', items: [{ text: 'subgroup7', items: [{ text: 'subgroup8', items: [] }] }] }] }] }] }];
const modify = (obj) => objectScan(['**.items'], {
rtn: 'count',
filterFn: ({ value, parent, property }) => {
if (Array.isArray(value) && value.length === 0) {
delete parent[property];
return true;
}
return false;
}
})(obj);
console.log(modify(data)); // returns number of deletions
// => 3
console.log(data);
// => [ { text: 'TreeRoot', items: [ { text: 'Subgroup1' }, { text: 'Subgroup2' }, { text: 'Subgroup3', items: [ { text: 'subgroup5', items: [ { text: 'subgroup6', items: [ { text: 'subgroup7', items: [ { text: 'subgroup8' } ] } ] } ] } ] } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>
Disclaimer: I'm the author of object-scan
This old question has been brought back up, and I think modern JS works well with recursion to yield a simple answer to do this in an immutable way:
const removeEmptyItems = (xs) =>
xs .map (({items = [], ... rest}) => ({
... rest,
... (items .length ? {items: removeEmptyItems(items)} : {})
}))
const tree = [{text: "TreeRoot", items: [{text: "Subgroup1", items: []}, {text: "Subgroup2", items: []}, {text: "Subgroup3",items: [{text: "subgroup5", items: [{text: "subgroup6", items: [{text: "subgroup7", items: [{text: "subgroup8", items: []}]}]}]}]}]}]
console .log (removeEmptyItems (tree))
.as-console-wrapper {max-height: 100% !important; top: 0}
This function simply maps over the array, keeping the rest of each object, and for its items
property, skipping it when it's empty and recurring when it's not.
I've got to say, when the question was first asked, this answer would have looked quite foreign! Now it's just normal JS!
This is my (probably not perfect) TypeScript with Generics solution.
It tries to infer first level keyof - maybe some more advanced Guru may help :)
type MyObject = { [index: string]: unknown };
export function removeProperties<T extends MyObject, K extends keyof T>(obj: T, propsToRemove: K[]): Omit<T, K> {
if (!obj) {
return obj;
}
const newObj = { ...obj } as MyObject;
Object.keys(newObj).forEach(key => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const value = obj[key] as any;
if (propsToRemove.includes(key as K)) {
delete newObj[key];
} else if (typeof value === 'object' && value != null) {
newObj[key] = removeProperties(value, propsToRemove);
}
});
return newObj as Omit<T, K>;
}