0

Say I have a json

jsonData = {
   "id":"dfd",
   "properties":{
      "Pri":"2",
      "Brief Description":"asdf",
      "Description":"",
      "tree":{
        "var": "2",
        "rav": "3"
      }
    }   
}

and a list

var variableArray = ['properties', 'tree', 'var'];

If I want to access the value of var and edit it. How would I do that while maintaining the value of jsonData?

I've tried

for (var i = 0; i < variableArray.length; i++) {
  jsonData = jsonData[variableArray[i]];
}
jsonData = 'new value';

But I can no longer access the whole jsonData.

What are some way to implement this?

Simon2233
  • 113
  • 1
  • 9
  • [What is the difference between JSON and Object Literal Notation?](https://stackoverflow.com/questions/2904131/what-is-the-difference-between-json-and-object-literal-notation) – Andreas Jul 11 '17 at 17:23
  • 2
    JSON is a *textual notation* for data exchange. [(More here.)](http://stackoverflow.com/a/2904181/157247) If you're dealing with JavaScript source code, and not dealing with a *string*, you're not dealing with JSON. – T.J. Crowder Jul 11 '17 at 17:25

2 Answers2

2

JavaScript doesn't have references to properties, which is effectively what you're trying to use there.

Instead, you can give yourself a method that will traverse an object and either retrieve or assign a property in it. Here's a quick and dirty in ES5 and earlier:

function accessPath(obj, path, value) {
    var o = obj;
    var i = 0;
    var last = path.length - 1;
    while (i < last) {
       o = o[path[i]];
       ++i;
    }
    if (arguments.length < 3) {
        // Getting
        return o[path[last]];
    } else {
        // Setting
        return o[path[last]] = value;
    }
}

Live example:

function accessPath(obj, path, value) {
    var o = obj;
    var i = 0;
    var last = path.length - 1;
    while (i < last) {
       o = o[path[i]];
       ++i;
    }
    if (arguments.length < 3) {
        // Getting
        return o[path[last]];
    } else {
        // Setting
        return o[path[last]] = value;
    }
}

var data = {
   "id":"dfd",
   "properties":{
      "Pri":"2",
      "Brief Description":"asdf",
      "Description":"",
      "tree":{
        "var": "2",
        "rav": "3"
      }
    }   
}

var path = ['properties', 'tree', 'var'];

console.log("Existing: " + accessPath(data, path));
accessPath(data, path, "new value");
console.log("Updated: " + accessPath(data, path));
console.log("Confirm: " + data.properties.tree.var);

Looks fairly similar in ES2015+, other than perhaps how you check if value is supplied.

Not pretty, but fairly efficient.

Actually, we can go further if we return an object with a getter and setter, which would look a bit like a property reference even though it isn't actually:

function makeAccessor(obj, path) {
    var o = obj;
    var i = 0;
    var last = path.length - 1;
    var lastName = path[last];
    while (i < last) {
       o = o[path[i]];
       ++i;
    }
    return {
        get value() {
            return o[lastName];
        },
        set value(value) {
            o[lastName] = value;
        }
    };
}

Then, getting the accessor:

var accessor = makeAccessor(data, path);

And using it:

console.log(accessor.value);
accessor.value = "new value";

function makeAccessor(obj, path) {
    var o = obj;
    var i = 0;
    var last = path.length - 1;
    var lastName = path[last];
    while (i < last) {
       o = o[path[i]];
       ++i;
    }
    return {
        get value() {
            return o[lastName];
        },
        set value(value) {
            o[lastName] = value;
        }
    };
}

var data = {
   "id":"dfd",
   "properties":{
      "Pri":"2",
      "Brief Description":"asdf",
      "Description":"",
      "tree":{
        "var": "2",
        "rav": "3"
      }
    }   
}

var path = ['properties', 'tree', 'var'];

var accessor = makeAccessor(data, path);

console.log("Existing: " + accessor.value);
accessor.value = "new value";
console.log("Updated: " + accessor.value);
console.log("Confirm: " + data.properties.tree.var);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I was getting errors calling the function with only two arguments, so I made small tweaks and it is working great. The code is working great, thanks! – Simon2233 Jul 11 '17 at 18:18
  • 1
    @Simon2233: That's odd, it works fine with two arguments in the live example above. BTW, I added a second option for you. :-) – T.J. Crowder Jul 11 '17 at 18:23
0

You could write little helpers read and write that do what you need – note data is not altered by use of read

const read = (o, [k,...ks]) =>
  o ? k === undefined ? o
                      : read (o[k], ks)
    : undefined
    
const write = (o, [k,...ks], v) =>
  o ? ks.length === 0 ? (o[k] = v, null)
                      : write (o[k], ks, v)
    : undefined
    
const data = {
  "id":"dfd",
  "properties":{
    "Pri":"2",
    "Brief Description":"asdf",
    "Description":"",
    "tree":{
      "var": "2",
      "rav": "3"
    }
  }   
}

// read
console.log (read (data, ['properties', 'tree', 'var']))
// => 2
console.log (read (data, ['foo', 'bar', 'barf']))
// => undefined (signals failure)

// write
console.log (write (data, ['properties', 'tree', 'var'], 'new value'))
// => null (signals success)
console.log (write (data, ['foo', 'bar', 'barf'], 'new value')) 
// => undefined (signals failure)

// read updated value
console.log (read (data, ['properties', 'tree', 'var'])) 
// => "new value"

By the way, to add to others' comments, JavaScript Object Notation is what JSON stands for. It's the Notation part that makes JSON different from "JSO" (JavaScript Object). More simply put, JSON is a string representation of a JavaScript Object. You'll know it's not JSON if it's not a string.

Mulan
  • 129,518
  • 31
  • 228
  • 259