4

I have the following object:

var object = {
    "property1": "value1",
    "property2": "value2",
    "subobject": {
        "property1": "value1",
        "property2": "value2",
        "subobject": {
            "property1": "value1",
            "property2": "value2",
            "subobject": {...
            }
        }
    }
}

I am trying to set one of the nested subobject properties, but the nested level is dynamic.

How can I dynamically set one of these nested properties without doing something like this: object.subobject.subobject = { ... }?

Edit: So to be more specific, I am trying to set one of the nested subobjects, but I won't know which one each time.

James Sumners
  • 14,485
  • 10
  • 59
  • 77
Kenny Thompson
  • 1,494
  • 12
  • 28

4 Answers4

7

Using recursion - refactor (thanks Rocket Hazmat)

This function works for me!:

/**
 * @obj: the json object to change
 * @access: string dot separates route to value
 * @value: new valu
 */
function setValue(obj,access,value){
    if (typeof(access)=='string'){
        access = access.split('.');
    }
    if (access.length > 1){
        setValue(obj[access.shift()],access,value);
    }else{
        obj[access[0]] = value;
    }
}

Having an object:

var jsonObject = {
    'name' : 'pepe',
    'links' : 
        {
        'link1' : 'one',
        'link2' : 'two',
        'link3' : 
            {
            'link31' : '3url',
            'link32' : '3url',
            'link33' : '3url'
            }
        }
 }

We can change a value easy with:

 setValue(jsonObject,'links.link3.link32','new value!!!');

Thanks

Raul Garcia
  • 71
  • 1
  • 3
  • There is a missing object initialisation. ```setValue(obj[access.shift()],access,value);``` should be ```setValue(obj[access.shift()] = {},access,value);```. – m_vdbeek Nov 27 '14 at 17:43
5

Let's use recursion!

function setNest(obj, level, val){
    if(level > 0){
        setNest(obj.subobject, level-1, val);
    }
    else{
        obj.subobject = val;
    }
}

Then call it like:

setNest(object, 2, {a: 12});
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • Wow, where is my brain today. I am not 100% sure why I didn't consider a recursive function... I was trying to do it in a loop. Thanks for the extra pair of eyes! – Kenny Thompson Jun 15 '12 at 16:51
  • You're welcome. Recursion can be tricky, but it's perfect for situations like this :-) – gen_Eric Jun 15 '12 at 16:52
  • I have tried to implement a solution that incorporates this, but it seems that this function is not updating the value of the original object. Any thoughts? – Kenny Thompson Jun 15 '12 at 17:58
  • @KennyThompson: It seems to work for me. http://jsfiddle.net/cYaML/1/ Object references in JavaScript are weird. Maybe you're dealing with a copy and the the *original* original object? – gen_Eric Jun 15 '12 at 18:18
  • After some digging, if appears that we are just altering the local variable obj and not the "object" variable (.. bad naming of the example i know.) – Kenny Thompson Jun 15 '12 at 18:24
  • I pass the original variable when I call the function – Kenny Thompson Jun 15 '12 at 18:25
  • @KennyThompson: References in JavaScript are weird. I thought objects were always passed by reference. Is there a case where that wouldn't happen? – gen_Eric Jun 15 '12 at 18:26
0

You can define your own Object methods; also I'm using underscore for brevity:

var _ = require('underscore');

// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
    if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
    var tmpo = this;
    while (i = addr.shift())
        tmpo = tmpo[i];
    return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
    if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
    this.pick(_.initial(addr))[_.last(addr)] = val;
};

Sample usage:

var obj = { 
           'foo': {
                   'bar': 0 }}

obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.pick('foo.bar'.split('.')); // equivalent as above, returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}
Keng
  • 854
  • 10
  • 10
0

You could try using Immutable.js, like so:

var immutableJsObject = Immutable.fromJS({
  inputs: {
    firstDepthNumberOne: {
      secondDepth: 'secondDepthValue'
    },
    firstDepthNumberTwo: 'hello'
  }
});

var newImmutableJsObject = immutableJsObject.setIn('inputs.firstDepthNumberOne.secondDepth'.split('.'), 'newValue');

newImmutableJsObject.toJS();
Con Antonakos
  • 1,735
  • 20
  • 23