You write:
Can we modify nested object values without modifying original object?
This sounds like a contradiction. When you mutate a nested object, then that is a mutation of the original object. It looks like you want immutability. This means that as soon as you need a change in a nested object, you create a copy of it that has the change, and this change will then bubble upwards, resulting in new copies of objects as they receive a new "child" object.
...without extra space
You cannot have both: either you mutate the original object (in its nested parts), or you create a new one. If you don't want the original object to mutate, then you obviously need space for the replacing object.
Some issues
Your input object has no boolean values. It has strings like "false" and "true", but those are not booleans. So I suggest to work with truly boolean values, without the quotes
Your code is not setting any property, so it can never mutate anything to your object
Your data has no "header" property, so that would be another reason why your code could not have changed anything.
The second argument you pass to the recursive call is i
, which is not the property you wanted to change. You should just pass propName
there.
Some assumptions I'll make
- The function is to return the original object when it finds that there is nothing change. This should also be the principle for nested objects.
- When the object has a property with the given name, but it is not a boolean, then it will not be changed. Only when that property value is a boolean there will be a change
- The change will toggle the boolean. So if it was
false
it should become true
and vice versa.
- No object should be mutated. If something changes, a new object should be created and returned.
Here is how I would do that. This snippet runs with 2 test cases: one that doesn't change anything, and another that does:
function changeBoolean(obj, propName) {
if (Object(obj) !== obj) return obj; // A primitive value: nothing changes
let changed = false;
let pairs = Object.entries(obj).map(function ([key, value]) {
let changedValue = key === propName && typeof value === "boolean" ? !value
: changeBoolean(value, propName);
changed ||= changedValue !== value;
return [key, changedValue];
});
return changed ? Object.fromEntries(pairs) : obj;
}
const config = {
header:{logo: true, nav: false, user: false},
searchResults:{listView: false, favorite: true, share: false, pagination: true, filters: true, sortBy: false},
sharedLinks:{},
learnerLinks:{},
lukePreview:{toc: false, controls: false},
lukeLaunch:{toc: false, controls: false},
misc:{import: true}
};
// Test 1
let header = changeBoolean(config, 'header');
console.log("header:", header === config ? "no change" : header);
// Test 2
let toc = changeBoolean(config, 'toc');
console.log("toc:", toc === config ? "no change" : toc);