1

I have a JSON that I am trying to change a value in

{
  string: "Hi",
  number: 0,
  boolean: false,
  object: {
    subString: "Hello",
    subNumber: 1,
    subBoolean: true,
    subObject: {
      subSubString: "Hello World"
    },
    subArray: ["-1", "-2", "-3"]
  },
  array: ["1", "2", "3"]
}

I have a piece of code that takes in a string with a JSON key and a new value (Eg. "object.subNumber", 5) and I am trying to get it to set the value of the subNumber value in the object ({ ... object: { ... subNumber: 5 ... } ... }), but I can't get it to work.

Here is what I have tried

var keys = args[0].split(".")
var value = args[1]

var configKey = config
            
keys.forEach(key => { configKey = configKey[key] })

configKey = value

which doesn't work. Is there some way to do this?

Kale
  • 179
  • 1
  • 1
  • 12
  • 1
    on the code showing what you tried, could you put the whole function, and an example input and desired output? – iagowp May 28 '21 at 18:28
  • 2
    The last iteration of your foreach copies a primitive, thus making a copy by value instead of reference. – Seblor May 28 '21 at 18:29
  • 1
    You can use this custom function ```let updateNestedValueOfObj = (data, string, value) => { let tempObj = data; // a moving reference to internal objects within obj const fieldList = string.split("."); const len = fieldList.length; for (var i = 0; i < len - 1; i++) { var elem = fieldList[i]; if (!tempObj[elem]) tempObj[elem] = {}; tempObj = tempObj[elem]; } if (!value) { return tempObj[fieldList[len - 1]]; } tempObj[fieldList[len - 1]] = value; };``` – Sunny May 28 '21 at 18:54
  • 1
    data = updateNestedValueOfObj(data,"object.subNumber",5) Just pass your string along with data and alue – Sunny May 28 '21 at 18:55
  • @Barmar - could you reopen? This question is about changing a deep nested json value rather than accessing one. Plus, I just spent the last 30 minutes coming up with a great scalable function to do just that. I think this solution would help future visitors – Kinglish May 28 '21 at 19:22
  • @Kinglish: reopened – georg May 28 '21 at 19:44
  • @georg - thanks! answer posted. hope it helps someone – Kinglish May 28 '21 at 19:46
  • 1
    Take a look at the npm package object-scan. It's made for this. Disclaimer: I'm the author – vincent May 29 '21 at 04:00

2 Answers2

2

You are iterating too far. You must stop before the last item:

function propertySetter(property, value) {
  var object = {
    string: "Hi",
    number: 0,
    boolean: false,
    object: {
      subString: "Hello",
      subNumber: 1,
      subBoolean: true,
      subObject: {
        subSubString: "Hello World"
      },
      subArray: ["-1", "-2", "-3"]
    },
    array: ["1", "2", "3"]
  };

  var keys = property.split(".");
  var propertyName = keys.pop();
  var propertyParent = object;
  while (keys.length > 0) {
    propertyParent = propertyParent[keys.shift()];
  }
  propertyParent[propertyName] = value;
  console.log("Modified object", object);
}

To test, call propertySetter("object.subNumber", 5).

Mario Varchmin
  • 3,704
  • 4
  • 18
  • 33
1

I was interested in your approach and came up with this scalable json deep-property edit function. This returns a copy of the original json

let orig_json = {
  string: "Hi",
  number: 0,
  boolean: false,
  object: {
    subString: "Hello",
    subNumber: 1,
    subBoolean: true,
    subObject: {
      subSubString: "Hello World"
    },
    subArray: ["-1", "-2", "-3"]
  },
  array: ["1", "2", "3"]
}

function changeValue(obj_path, value, json) {
  let keys = obj_path.split(".")
  let obj = { ...json
    },
    tmpobj = {},
    prevobj = {}
  for (let x = keys.length - 1; x >= 0; x--) {
    if (x == 0) {
      obj[keys[0]] = tmpobj
    } else {
      let toeval = 'json.' + keys.slice(0, x).join('.');
      prevobj = { ...tmpobj
      }
      tmpobj = eval(toeval);
      if (x == keys.length - 1) tmpobj[keys[x]] = value
      else {
        tmpobj[keys[x]] = prevobj
      }
    }
  }
  return obj
}

let newjson = changeValue("object.subObject.subSubString", "Goodbye world", orig_json);
console.log(newjson)
Kinglish
  • 23,358
  • 3
  • 22
  • 43