Search For Keys In Nested Object And Delete Them
Solution 1:
The problem with using delete
is that you are essentially altering the original object. This can cause problems if you are using the same Object reference in different locations.
The JSON method will not work, since the resulting JSON string is not valid after removing parts of the string. You could make this work but is much more prone to errors.
I always like to make this kind of function return a new instance of the Object.
functionremoveKeys(obj, keys) {
if (Array.isArray(obj)) return obj.map(item =>removeKeys(item, keys));
if (typeof obj === 'object' && obj !== null) {
returnObject.keys(obj).reduce((previousValue, key) => {
return keys.includes(key) ? previousValue : { ...previousValue, [key]: removeKeys(obj[key], keys) };
}, {});
}
return obj;
}
It is also possible to first shallow copy the Object and use delete
on the new Object instead of using reduce
.
functionremoveKeys(obj, keys) {
if (Array.isArray(obj)) return obj.map((item) =>removeKeys(item, keys));
if (typeof obj === "object" && obj !== null) {
returnObject.keys(obj).reduce((previousValue, key) => {
return keys.includes(key)
? previousValue
: { ...previousValue, [key]: removeKeys(obj[key], keys) };
}, {});
}
return obj;
}
const data = {
details: [
{
userId: "user01",
documents: [
{
document: {
id: "doc_pp_01",
type: "pp",
number: "222333444",
personName: {
first: "JAMES",
middle: "JOHNIE",
last: "SMITH"
},
nationality: "AL",
dateOfBirth: "1990-01-01",
issuingCountry: "AL",
expiryDate: "2025-01-01",
gender: "MALE"
}
}
]
}
],
criteria: {
"id:": "AB1234",
fullName: "James Johnie Smith"
}
};
console.log(
"Without `fullName` and `details`",
removeKeys(data, ["fullName", "details"])
);
console.log("Without `id:` and `gender`", removeKeys(data, ["id:", "gender"]));
Solution 2:
You can make your function recursive with only a few changes. I changed deleteKeys
into a parameter, which I find cleaner, but is not essential.
functioncleanData(data, deleteKeys) {
// There is nothing to be done if `data` is not an object,// but for example "user01" or "MALE".if (typeof data != "object") return;
if (!data) return; // null objectfor (const key in data) {
if (deleteKeys.includes(key)) {
delete data[key];
} else {
// If the key is not deleted from the current `data` object,// the value should be check for black-listed keys.cleanData(data[key], deleteKeys);
}
}
}
const data = {
"details": [{
"userId": "user01",
"documents": [{
"document": {
"id": "doc_pp_01",
"type": "pp",
"number": "222333444",
"personName": {
"first": "JAMES",
"middle": "JOHNIE",
"last": "SMITH"
},
"nationality": "AL",
"dateOfBirth": "1990-01-01",
"issuingCountry": "AL",
"expiryDate": "2025-01-01",
"gender": "MALE"
}
}]
}],
"criteria": {
"id:": "AB1234",
"fullName": "James Johnie Smith"
}
};
cleanData(data, ["details", "fullName"]);
console.log(data);
One thing to keep in mind is that delete
mutates the existing object. This means that returning data
is not essential.
Functions like reverse()
and sort()
do return the array, even though they mutate the existing array. This often leads to bugs/issues with novice JavaScript programmers.
const foo = ["a", "b", "c"];
const bar = foo.reverse();
The above code suggests that foo
and bar
are two different arrays. This is not the case. They both refer to the same array, thus foo
and bar
are now both reversed. For this reason I often don't use the return value of mutating code on Stack Overflow. The following example much better displays that the code is mutating.
const foo = ["a", "b", "c"];
foo.reverse();
This is the reason I would personally not return from cleanData()
. This forces users to notice that your function is mutating.
There is also something to be said for returning the input, since you can then chain the return value with other methods/functions. But my personal take is that the disadvantage of misunderstanding outweighs the advantage of chaining. Especially on a "learning" platform like Stack Overflow.
Solution 3:
If you want to delete the empty arrays or objects after deleting keys, the following implementation will work.
functioncleanData(data, deletingKeys) {
functionisEmpty(obj) {
if (obj === null) returntrue;
if (Array.isArray(obj))
return obj.length === 0;
if (typeof obj === "object")
returnObject.keys(obj).length === 0;
}
functionremoveKeyFrom(aData) {
if (Array.isArray(aData)) {
const done = aData.reduce((accum, ele) => {
const done = removeKeyFrom(ele);
if (!isEmpty(done))
accum.push(done);
return accum;
}, []);
return done.length > 0 ? done : null;
}
if (typeof aData === "object" && aData !== null) {
const done = Object.keys(aData).reduce((accum, key) => {
if (!deletingKeys.includes(key)) {
const done = removeKeyFrom(aData[key]);
if (!isEmpty(done)) // required for empty object element
accum[key] = done;
}
return accum;
}, {});
return (Object.keys(done).length > 0) ? done : null;
}
return aData;
}
returnremoveKeyFrom(data);
}
Suppose that your data and the deleting criteria are as following:
constdata = {
"details2": [{
"userId": "user01",
// `documents` should be removed since the value is the array of an empty object"documents": [{
"details": { // this should be removed"id": "doc_pp_01",
"type": "pp",
"number": "222333444",
"personName": {
"first": "JAMES",
"middle": "JOHNIE",
"last": "SMITH"
},
"nationality": "AL",
"dateOfBirth": "1990-01-01",
"issuingCountry": "AL",
"expiryDate": "2025-01-01",
"gender": "MALE"
}
}]
}],
"criteria": {
"id:": "AB1234",
"fullName": "James Johnie Smith",
"shouldBeRemoved": { // this should be removed since the value will be the empty object"details": {
"whatever": 1234
}
}
}
};
const deleteKeys = ["details", "fullName"];
Calling cleanData(data, deleteKeys)
will result in
{"details2":[{"userId":"user01"}],"criteria":{"id:":"AB1234"}}
instead of
{"details2":[{"userId":"user01","documents":[{}]}],"criteria":{"id:":"AB1234","shouldBeRemoved":{}}}
Here is the complete code to run.
functioncleanData(data, deletingKeys) {
functionisEmpty(obj) {
if (obj === null) returntrue;
if (Array.isArray(obj))
return obj.length === 0;
if (typeof obj === "object")
returnObject.keys(obj).length === 0;
}
functionremoveKeyFrom(aData) {
if (Array.isArray(aData)) {
const done = aData.reduce((accum, ele) => {
const done = removeKeyFrom(ele);
if (!isEmpty(done))
accum.push(done);
return accum;
}, []);
return done.length > 0 ? done : null;
}
if (typeof aData === "object" && aData !== null) {
const done = Object.keys(aData).reduce((accum, key) => {
if (!deletingKeys.includes(key)) {
const done = removeKeyFrom(aData[key]);
if (!isEmpty(done)) // required for empty object element
accum[key] = done;
}
return accum;
}, {});
return (Object.keys(done).length > 0) ? done : null;
}
return aData;
}
returnremoveKeyFrom(data);
}
const data = {
"details2": [{
"userId": "user01",
// `documents` should be removed since the value is the array of empty object"documents": [{
"details": { // this should be removed"id": "doc_pp_01",
"type": "pp",
"number": "222333444",
"personName": {
"first": "JAMES",
"middle": "JOHNIE",
"last": "SMITH"
},
"nationality": "AL",
"dateOfBirth": "1990-01-01",
"issuingCountry": "AL",
"expiryDate": "2025-01-01",
"gender": "MALE"
}
}]
}],
"criteria": {
"id:": "AB1234",
"fullName": "James Johnie Smith",
"shouldBeRemoved": { // this should be removed since the value is the empty object."details": {
"whatever": 1234
}
}
}
};
const deleteKeys = ["details", "fullName"];
constprint = obj => JSON.stringify(obj, null, 2);
console.log(print(cleanData(data, deleteKeys)));
Post a Comment for "Search For Keys In Nested Object And Delete Them"