Simon2233
Simon2233

Reputation: 113

Traverse down a JSON depending on length of array

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?

Upvotes: 0

Views: 66

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075209

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

Upvotes: 2

Mulan
Mulan

Reputation: 135397

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.

Upvotes: 0

Related Questions