Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I hope someone can help me with this Javascript.

I have an Object called "Settings" and I would like to write a function that adds new settings to that object.

The new setting's name and value are provided as strings. The string giving the setting's name is then split by the underscores into an array. The new setting should get added to the existing "Settings" object by creating new nested objects with the names given by each part of the array, except the last part which should be a string giving the setting's value. I should then be able to refer to the setting and e.g. alert its value. I can do this in a static way like this...

var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");

Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;

alert(Settings.Modules.Mediaplayers.Video.Plugin);

... the part that creates the nested objects is doing this ...

Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";

However, as the number of parts that make up the setting name can vary, e.g. a newSettingName could be "Modules_Floorplan_Image_Src", I'd like to do this dynamically using a function such as...

createSetting (newSettingNameArray, newSettingValue);

function createSetting(setting, value) {
    // code to create new setting goes here
}

Can anyone help me work out how to do this dynamically?

I presume there has to be a for...loop in there to itterate through the array, but I haven't been able to work out a way to create the nested objects.

If you've got this far thanks very much for taking the time to read even if you can't help.

David

share|improve this question

5 Answers

up vote 12 down vote accepted
function assign(obj, keyPath, value) {
   lastKeyIndex = keyPath.length-1;
   for (var i = 0; i < lastKeyIndex; ++ i) {
     key = keyPath[i];
     if (!(key in obj))
       obj[key] = {}
     obj = obj[key];
   }
   obj[keyPath[lastKeyIndex]] = value;
}
share|improve this answer
Well, I wrote pretty much that same thing, except that I was going to explain newSettingName.split('_'). I don't see the point behind duplicate answers, so there. Maybe explain better your answer though. – Christian Mar 30 '11 at 9:56
Thanks very much for the perfect answer ! – David Mar 30 '11 at 10:25

Put in a function, short and fast.

var createNestedObject = function( base, names ) {
    for( var i = 0; i < names.length; i++ ) {
        base = base[ names[i] ] = base[ names[i] ] || {};
    }
};

// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.

Skips already existing parts of the hierarchy. Useful if you are not sure whether the hierarchy was already created.

Or:

A fancier version where you can directly assign the value to the last object in the hierarchy, and you can chain function calls because it returns the last object.

// Function: createNestedObject( base, names[, value] )
//   base: the object on which to create the hierarchy
//   names: an array of strings contaning the names of the objects
//   value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
    // If a value is given, remove the last name and keep it for later:
    var lastName = arguments.length === 3 ? names.pop() : false;

    // Walk the hierarchy, creating new objects where needed.
    // If the lastName was removed, then the last object is not set yet:
    for( var i = 0; i < names.length; i++ ) {
        base = base[ names[i] ] = base[ names[i] ] || {};
    }

    // If a value was given, set it to the last name:
    if( lastName ) base = base[ lastName ] = value;

    // Return the last object in the hierarchy:
    return base;
};

// Usages:

createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.

createNestedObject( window, ["shapes", "rectangle", "width"], 300 );
// Now we have: window.shapes.rectangle.width === 300

createNestedObject( window, "shapes.rectangle.height".split('.'), 400 );
// Now we have: window.shapes.rectangle.height === 400

Note: if you need values different than standard objects (ie. not {}) in your hierarchy, see also TimDog's answer.

Edit: uses regular loops instead of for...in loops. It's safer in cases where a library modifies the Array prototype.

share|improve this answer
I hope you realize that you're answering a question for which the accepted answer is already over 15 months old... – Bart Jul 11 '12 at 20:28
2  
@Bart: Shouldn't I ? – jlgrall Jul 11 '12 at 20:38
It's okay, it's just not very likely the person who asked the question is still looking for an answer. Your answer is still good (and different from the rest) so I gave you some thumbs up on it. – Bart Jul 11 '12 at 20:41
Thanks for the nice code! – Fawkes5 Aug 10 '12 at 5:18
Actually, this is the best answer as it behaves as expected. – Adrien Giboire Oct 1 '12 at 14:50
show 4 more comments

Here is a simple tweak to jlgrall's answer that allows setting distinct values on each element in the nested hierarchy:

var createNestedObject = function( base, names, values ) {
    for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};

Hope it helps.

share|improve this answer

try using recursive function:

function createSetting(setting, value, index) {
  if (typeof index !== 'number') {
    index = 0;
  }

  if (index+1 == setting.length ) {
    settings[setting[index]] = value;
  }
  else {
    settings[setting[index]] = {};
    createSetting(setting, value, ++index);
  }
}
share|improve this answer

I think, this is shorter:

Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");

a = Settings;
for (var i = 0 in newSettingNameArray) {
    var x = newSettingNameArray[i];
    a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
    a = a[x];
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.