Reputation: 227240
Let's say I have a string representing the keys in an object. Here's an example:
var obj = {
test: 12,
high: {
sky: {
val: 14
}
},
low: [1, 2, 3]
},
keys = 'high.sky.val';
So, I want to set the value of obj.high.sky.val
(with 'high.sky.val'
being in a string).
I know how to read the value (though, this may not be the best way):
var keyPieces = keys.split('.'), value = obj;
keyPieces.forEach(function(x){
value = value[x];
});
console.log(value); // 14
I can't figure out how to set obj.high.sky.val
(without using eval
).
How can I set the value of a property in an object, if that key is a string?
Upvotes: 1
Views: 5735
Reputation: 311
I think this is an important question and I needed a variant, that allows for arrays nested in the objects AND for deep creation of non-existant fields. I know you specified 'object' but in reality an object can also hold arrays. In this situation the otherwise excellent code by @Marshall didn't help me out. So I started fiddling and got the following code (hope this helps anyone):
function objGet(obj, keyString) {
// allow for arrays, returns undefined for non-existant-fields.
var keys=[{label:"",type:"field",is_array:false}], current_key=0;
for(var i=0;i<keyString.length;i++)
{
var c=keyString.charAt(i);
switch(c)
{
case ".":
current_key++;
keys[current_key]={label:"",type:"field",is_array:false};
break;
case "[":
keys[current_key].is_array=true;
current_key++;
keys[current_key]={label:"",type:"index",is_array:false};
break;
case "]":
break;
default:
keys[current_key].label+=c;
}
}
var part=obj;
for(i = 0; i < keys.length; i++)
{
var label=keys[i].label;
if(i==keys.length-1)
{
return part[label];
}else{
if(part[label] === undefined)
{
return undefined;
}
part = part[label];
}
}
}
function objSet(obj, keyString, val) {
// allows for arrays, deep creates non-existant fields.
var keys=[{label:"",type:"field",is_array:false}], current_key=0;
for(var i=0;i<keyString.length;i++)
{
var c=keyString.charAt(i);
switch(c)
{
case ".":
current_key++;
keys[current_key]={label:"",type:"field",is_array:false};
break;
case "[":
keys[current_key].is_array=true;
current_key++;
keys[current_key]={label:"",type:"index",is_array:false};
break;
case "]":
break;
default:
keys[current_key].label+=c;
}
}
var part=obj;
for(i = 0; i < keys.length; i++)
{
var label=keys[i].label;
if(i==keys.length-1)
{
part[label] = val;
}else{
if(part[label] === undefined)
{
// we need to create it for deep set!
if(keys[i].is_array)
{
part[label]=[];
}else{
part[label]={};
}
}
part = part[label];
}
}
}
// TESTS
var obj = {
test: 12,
high: {
sky: {
val: 14
}
},
kneedeep: [
{something:"som0",something_else:"elze0"},
{something:"som1",something_else:"elze1"}
],
low: [1, 2, 3]
};
var obj_str=JSON.stringify(obj);
console.log("testing with object: "+obj_str);
// TEST GET
console.log("test: "+objGet(obj, 'test')); // returns 999
console.log("high.sky.non.existant: "+objGet(obj, 'high.sky.non.existant')); // returns undefined
console.log("kneedeep[0].something: "+objGet(obj, 'kneedeep[0].something')); // returns "som0"
console.log("kneedeep[1].something_else: "+objGet(obj, 'kneedeep[1].something_else')); // returns "elze1"
console.log("high.sky.val: "+objGet(obj, 'high.sky.val')); // returns 14
console.log("low[0]: "+objGet(obj, 'low[0]')); // returns 1
// TEST SET
objSet(obj, 'test', 999); // return 999
console.log("result SET 'test', 999:");
console.log(JSON.stringify(obj));
obj=JSON.parse(obj_str); // reset the object
objSet(obj, 'high.sky.non.existant', 1234); // creates the necessary objects.
console.log("result SET 'high.sky.non.existant', 1234:");
console.log(JSON.stringify(obj));
obj=JSON.parse(obj_str); // reset the object
objSet(obj, 'high.sky.val', 111);
console.log("result SET 'high.sky.val', 111:");
console.log(JSON.stringify(obj));
obj=JSON.parse(obj_str); // reset the object
objSet(obj, 'kneedeep[0].something', 111);
console.log("result SET 'kneedeep[0].something', 111:");
console.log(JSON.stringify(obj));
obj=JSON.parse(obj_str); // reset the object
objSet(obj, 'kneedeep[1].something_else', 1234);
console.log("result SET 'kneedeep[1].something_else', 1234:");
console.log(JSON.stringify(obj));
Upvotes: 0
Reputation: 120516
function setDeep(el, key, value) {
key = key.split('.');
var i = 0, n = key.length;
for (; i < n-1; ++i) {
el = el[key[i]];
}
return el[key[i]] = value;
}
function getDeep(el, key) {
key = key.split('.');
var i = 0, n = key.length;
for (; i < n; ++i) {
el = el[key[i]];
}
return el;
}
and you can use it thus:
setDeep(obj, 'high.sky.val', newValue);
Upvotes: 2
Reputation: 76736
Just for fun:
function setKey(key, value, targetObject) {
var keys = key.split('.'), obj = targetObject || window, keyPart;
while ((keyPart = keys.shift()) && keys.length) {
obj = obj[keyPart];
}
obj[keyPart] = value;
}
Edit: The previous version wouldn't have worked with "no-dot" keys... Fixed.
Upvotes: 6
Reputation: 4766
You can use a pair of functions to set and get values. I just threw together an example.
objGet
takes an object and key string. It will attempt to get the value. If it can't find it it will return undefined.
objSet
takes an object, key string and value. It will attempt to find and set the value. If it can't (because of a bad key string) it will return undefined. Else it returns the value passed.
function objGet(obj, keyString) {
for(var keys = keyString.split('.'), i = 0, l = keys.length; i < l; i++) {
obj = obj[keys[i]];
if(obj === undefined) return undefined;
}
return obj;
}
function objSet(obj, keyString, val) {
for(var keys = keyString.split('.'), i = 0, l = keys.length; i < l - 1; i++) {
obj = obj[keys[i]];
if(obj === undefined) return undefined;
}
if(obj[keys[l - 1]] === undefined) return undefined;
obj[keys[l - 1]] = val;
return val;
}
//// TESTING
var obj = {
test: 12,
high: {
sky: {
val: 14
}
},
low: [1, 2, 3]
};
objGet(obj, 'test'); // returns 12
objGet(obj, 'high.sky.val'); // returns 14
objGet(obj, 'high.sky.non.existant'); // returns undefined
objSet(obj, 'test', 999); // return 999
obj.test; // 999
objSet(obj, 'high.sky.non.existant', 1234); // returns undefined
obj.high.sky; // { val: 14 }
objSet(obj, 'high.sky.val', 111); // returns 111
obj.high.sky; // { val: 111 }
Upvotes: 2
Reputation: 324640
I actually had to make a couple of functions to achieve this end when working in GameMaker:HTML5
function js_get(varname) {
if( varname.indexOf(".") < 0)
return window[varname];
else {
var curr = window, steps = varname.split("."), next;
while(next = steps.shift()) curr = curr[next];
return curr;
}
}
function js_set(varname,value) {
if( varname.indexOf(".") < 0)
window[varname] = value;
else {
var curr = window, steps = varname.split("."), next, last = steps.pop();
while(next = steps.shift()) curr = curr[next];
curr[last] = value;
}
}
This works because objects are passed by reference in JS.
Upvotes: 5