2

I have a json object like this:

var obj = {
    "glossary": {
        "title": "example glossary",
        "GlossDiv": {
            "title": "S",
            "GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
                    "SortAs": "SGML",
                    "GlossTerm": "Standard Generalized Markup Language",
                    "Acronym": "SGML",
                    "Abbrev": "ISO 8879:1986",
                    "GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
                        "GlossSeeAlso": ["GML", "XML"]
                    },
                    "GlossSee": "markup"
                }
            }
        }
    }
}

This JSON can be any valid JSON. Now I want to access and update some property based on an array available to me like this:

['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'SortAs'] // any dynamic but valid path

So given the above array, I want to access

obj['glossary']['GlossDiv']['GlossList']['GlossEntry']['SortAs']

so that I can update its value. I have config(or any such json) as an instance variable, so I want to directly update that rather than creating a new json and then replacing the entire config object. I have access to jQuery, underscore, backbone and of course plain old JS.

6
  • 3
    Your object isn't currently valid json. Commented Nov 3, 2016 at 15:53
  • You should have posted that in the first place. Commented Nov 3, 2016 at 16:05
  • Updated the object so that it is a valid json now. Sometimes feelings get ahead of syntax. Thanks for pointing out though. Commented Nov 3, 2016 at 16:06
  • 1
    now you path would be ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'SortAs']? Commented Nov 3, 2016 at 16:10
  • I updated my answer to show how to use Nina's answer to set the value. Commented Nov 3, 2016 at 16:27

3 Answers 3

2

You could reduce the object with walking the path.

function getValue(object, path) {
    return path.reduce(function (o, k) {
        return (o || {})[k];
    }, object);
}

var object = { glossary: { title: "example glossary", GlossDiv: { title: "S", GlossList: { GlossEntry: { ID: "SGML", SortAs: "SGML", GlossTerm: "Standard Generalized Markup Language", Acronym: "SGML", Abbrev: "ISO 8879:1986", GlossDef: { para: "A meta-markup language, used to create markup languages such as DocBook.", GlossSeeAlso: ["GML", "XML"] }, GlossSee: "markup" } } } } };

console.log(getValue(object, ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'SortAs']));

Sign up to request clarification or add additional context in comments.

1 Comment

After the question edit, your answer is definitely what he needs.
1

Though you are using Underscore, Lodash is 100% compatible with Backbone and can replace Underscore and it offers _.get and _.set for your specific case.

The path can be in an array or it can be a string. It even handles accessing array indexes.

_.get(object, path, [defaultValue])
var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// => 3

_.get(object, ['a', '0', 'b', 'c']);
// => 3

_.get(object, 'a.b.c', 'default');
// => 'default'

_.set creates missing objects and arrays along the path if they do not exist.

_.set(object, path, value)
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4

_.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5

There's even a _.result function which calls the value if it's a function.

_.result(object, path, [defaultValue])
var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };

_.result(object, 'a[0].b.c1');
// => 3

_.result(object, 'a[0].b.c2');
// => 4

_.result(object, 'a[0].b.c3', 'default');
// => 'default'

_.result(object, 'a[0].b.c3', _.constant('default'));
// => 'default'

Based on Nina's answer, to set the value, you could do something like the following, having a path which stops at before the last key, and use the last key to update the object manually.

function getValue(object, path) {
    return path.reduce(function (o, k) {
        return (o || {})[k];
    }, object);
}

var obj = { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": { "para": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML"] }, "GlossSee": "markup" } } } } };


var target = getValue(obj, ['glossary', 'GlossDiv']);

target.title = "test";

console.log(obj);

Or as a new function, which takes a value directly and does all the job.

function setValue(object, path, value) {
  var target = path.slice(0, -1).reduce(function(obj, key) {
    return (obj || {})[key];
  }, object);
  target[path[path.length-1]] = value;
}

var obj = { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": { "para": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML"] }, "GlossSee": "markup" } } } } };



setValue(obj, ['glossary', 'GlossDiv', 'GlossList', 'GlossEntry', 'GlossSee'], "my test value");

console.log(obj);

Comments

0

Nina's answer would be my preferred way of doing this. For those not yet familiar with functional programming or reducing, a simple forEach will do the trick. Good for comparison.

var config = {
  name: "foo",
  email: "bar",
  photo: {
    small: "somelink",
    large: "largelink"
  }
};

var keys = ['photo', 'large'];
var prevRef = config, ref;

keys.forEach(function(val, index) {
  console.log(val);
  ref = prevRef[val];
  prevRef = ref;
});

console.log(ref);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.