3

How can I access myobject[path][to][obj] via a string path.to.obj? I want to call a function Settings.write('path.to.setting', 'settingValue') which would write 'settingValue' to settingObj[path][to][setting]. How can I do this without eval()?

I finally figured it out around the same time as one of the users below answered it. I'll post mine here with documentation on how it works if anyone is interested.

(function(){
    var o = {}, c = window.Configure = {};

    c.write = function(p, d)
    {
        // Split the path to an array and assaign the object
        // to a local variable
        var ps = p.split('.'), co = o;

        // Iterate over the paths, skipping the last one
        for(var i = 0; i < ps.length - 1; i++)
        {
            // Grab the next path's value, creating an empty 
            // object if it does not exist
            co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
        }

        // Assign the value to the object's last path
        co[ps[ps.length - 1]] = d;
    }

    c.read = function(p)
    {
        var ps = p.split('.'), co = o;
        for(var i = 0; i < ps.length; i++)
        {
            co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
        }
        return co;
    }
})();

The reason I was having problems is you have to skip the last path. If you include the last path, you end up just assigning an arbitrary object.

LordZardeck
  • 7,953
  • 19
  • 62
  • 119

2 Answers2

3

You can use str.split() for that:

path = pathString.split('.');
settingObj[path[0]][path[1]][path[2]] = settingValue;

str.split() will separate the variable into an array, split at the locations of the character you specify.

This is just the root of the operation of course, and assumes proper input.

Edit: Since the paths need to be of variable length, here's an updated version edited from the syntax you've shown above:

(function(){
    var o = {}, c = window.Configure = {};

    c.write = function(p, d)
    {
        var ps = p.split('.'), co = o;
        for(var i = 0; i < ps.length - 1; i++)
        {
            co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
        }
        co[ps[ps.length-1]] = d;
    }

    c.read = function(p)
    {
        var ps = p.split('.'), co = o;
        for(var i = 0; i < ps.length; i++)
        {
            co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
        }
        return co;
    }

    c.write('path.to.property', 'setting');
    alert(c.read('path.to.property'));
})();
Rick
  • 1,268
  • 9
  • 8
  • but you never know exactly how many parts to the path there are. Could be 2, could be 6. – LordZardeck Feb 18 '12 at 03:24
  • My bad, I assumed it was some sort of pre-existing setting or something. I put the code in a fiddle here: http://jsfiddle.net/HVh7X/, which I started before seeing your edit, so I'll update my answer in a minute. – Rick Feb 18 '12 at 03:45
  • lol, yeah, just figured it out. I needed to skip the last path, cause I was just assigning a arbitrary value. As soon as you edit your answer, I'll accept it. – LordZardeck Feb 18 '12 at 03:50
  • warning, your above code will overwrite any object, even if you are trying to append to it. Line 9 and 10 should be changed to `co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};` – LordZardeck Feb 18 '12 at 03:57
  • Good point. I hadn't thought of pre-existing settings paths. Edited, should be right now. Thanks for marking it as answer anyway. :) – Rick Feb 18 '12 at 04:05
2

Your variable co contains a reference to your property, but when you update the value of co with co = p you are just overwriting the value of co, not the value of the property. You need to keep a reference to the last key, and use that key to set the value:

http://jsfiddle.net/gilly3/a2p8m/1/

c.write = function(p, d) 
{ 
    var ps = p.split('.'), co = o, key; 
    for(var i = 0; i < ps.length; i++) 
    { 
        if (key)
        {
            co = (key in co) ? co[key] : co[key] = {};
        }
        key = ps[i];
    } 
    co[key] = d; 
    console.log(co); 
    console.log(o); 
} 

You may want to use key in co instead of just doing a "truthy" check. Otherwise, a value of 1, will be retained while a value of 0 will be overwritten with {}. Then again, either of those values would result in a runtime error for the above code, since you can't write a property onto a number. Best would be to check typeof co[key].

co = co[key] && /object|function/.test(typeof co[key]) ? co[key] : co[key] = {};

(I know I'm a little late with my answer, but I figure after putting all that effort in typing with my thumbs, I might as well include it anyway)

I suspect you are encountering a grouping error. Try moving the end paren in your ternary expression to the end of the line:

co = (co[ps[i]] ? co[ps[i]] : co[ps[i]] = {});

I'd test this myself but I'm answering from my mobile and jsfiddle is tedious when typing with just thumbs.

gilly3
  • 87,962
  • 25
  • 144
  • 176