I want to modify a property in deeply nested object in Javascript and return the modified object. For eg, I am rendering checkboxes in my application and the structure looks like below,
{
level1: {
name: 'Level 1',
key: 'level1',
checked: false,
subLevels: {
level2: {
name: 'Level 2',
key: 'level2',
checked: false,
subLevels: {
level3: {
name: 'Level 3',
key: 'level3',
checked: true,
},
level4: {
name: 'Level 4',
key: 'level4',
checked: false,
}
}
}
}
}
}
I am rendering the above structure like below,
Now, if a user clicks on any of the checkboxes, I want to return the modified object with the updated state, so let's say if the user clicked on level4 checkbox, I want the below object to be returned. Also, I have the key corresponding to the checked checkbox, so for above scenario, i have 'level4'.
{
level1: {
name: 'Level 1',
key: 'level1',
checked: false,
subLevels: {
level2: {
name: 'Level 2',
key: 'level2',
checked: false,
subLevels: {
level3: {
name: 'Level 3',
key: 'level3',
checked: true,
},
level4: {
name: 'Level 4',
key: 'level4',
checked: true,
}
}
}
}
}
}
I wrote the below function to modify the value, but facing difficulty in returning a new object. Also, the object could be deeply nested to any level,
function changeVal(obj, checkedKey) {
for(const key in obj) {
if(key === 'subLevels' && typeof obj.subLevels === 'object') {
changeVal(obj[key].subLevels);
}
if(key === checkedKey) {
obj[key].checked = !obj[key].checked;
}
}
}
Could you please help out?
CodePudding user response:
Presented below is one possible way to achieve the desired objective.
Code Snippet
const myUpdate = (obj, k) => (
[k] in obj
? obj[k].checked = !obj[k].checked
: Object.values(obj).forEach(
v => myUpdate(v?.subLevels ?? {}, k)
),
obj
);
/* EXPLANATION of the code ---
// method to update a "cloned" object
// the caller passes a deep-cloned object
// by using "structuredClone()"
const myUpdate = (obj, k) => {
// if "k" (say "level4") is in "obj"
if ([k] in obj) {
// just flip the "checked" prop (false to true, or vice-versa)
obj[k].checked = !obj[k].checked
} else {
// else, recursive call using the "subLevels" prop
// if there are no values in obj or no "subLevels"
// simply pass empty object for recursion
Object.values(obj).forEach(
v => myUpdate(v?.subLevels ?? {}, k)
)
};
// always return "obj"
return obj;
};
*/
const dataObj = {
level1: {
name: 'Level 1',
key: 'level1',
checked: false,
subLevels: {
level2: {
name: 'Level 2',
key: 'level2',
checked: false,
subLevels: {
level3: {
name: 'Level 3',
key: 'level3',
checked: true,
},
level4: {
name: 'Level 4',
key: 'level4',
checked: false,
}
}
}
}
}
};
console.log(
'\n\n setting level-4 to true :\n',
myUpdate(structuredClone(dataObj), 'level4'),
'\n\n setting level-3 to false :\n',
myUpdate(structuredClone(dataObj), 'level3'),
'\n\nand now the existing obj, un-altered:\n',
dataObj,
);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Comments added to the snippet above.
CodePudding user response:
var data = {
level1: {
name: 'Level 1',
key: 'level1',
checked: false,
subLevels: {
level2: {
name: 'Level 2',
key: 'level2',
checked: false,
subLevels: {
level3: {
name: 'Level 3',
key: 'level3',
checked: true,
},
level4: {
name: 'Level 4',
key: 'level4',
checked: false,
}
}
}
}
}
}
var newJsonObject = traverseNesteddata(data, "level4");
console.log(newJsonObject);
var keepTheLevel4;
function traverseNesteddata(data, checkedKey){
for(var singleValue in data){
if(typeof data[singleValue] == 'object'){
traverseNesteddata(data[singleValue], checkedKey);
}else{
if(data[singleValue] === checkedKey)
{
if(data.checked === false)
data.checked = true;
else
data.checked = false;
}}
}
return data;
}