Akhil Raghav
Akhil Raghav

Reputation: 343

How can we modify nested object values without modifying original object in JavaScript?

How can I change all nested object values to "true" without using extra space Can anyone help me in this. I have tried in this way but I am not getting the logic to handle the nested object

P.S: Please don't concentrate about "true" or "false" is in string because it is mock data,I just want logic to implement which I am failing to do.

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"}
}

function changeBoolean(obj,propName){
  for(let i in obj){
    if( typeof obj[i] === 'object'){
       changeBoolean(obj[i],i)
    }
  }
  return obj
}
console.log(changeBoolean(config,'header'))

Upvotes: 3

Views: 1015

Answers (3)

Yosvel Quintero
Yosvel Quintero

Reputation: 19070

You can do:

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" }
}

const changeBoolean = o => Object.entries(o).reduce((a, [k, v]) => {
  a[k] = typeof v === 'object' ? changeBoolean(v)  : 'true'
  return a
}, {})
const newConfig = changeBoolean(config)

console.log(newConfig)

Upvotes: 0

trincot
trincot

Reputation: 350310

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);

Upvotes: 0

Owen Young
Owen Young

Reputation: 322

Try to assign the function's result, like this:


function changeBoolean(obj, propName) {
  for (let i in obj) {
    if (typeof obj[i] === "object") {
      obj[i] = changeBoolean(obj[i], propName);
    } else {
      obj[i] = "true";
    }
  }
  return obj;
}

Upvotes: 2

Related Questions