This is a reverse question to this question.
Given an object x={a:1,b:2}
and a string c.d=3
, modify object x to the following:
What about this :
function setObj (str, value, obj) {
var ref = obj, keys = str.split('.');
while (keys.length) {
var currentKey = keys.shift();
ref[currentKey] = keys.length ? (ref[currentKey] ? ref[currentKey] : {}) : value;
ref = ref[currentKey];
}
}
Example with an input object using (could be some form values extracted with $.serializeArray)
var serializedInputs = [
{name: 'fruits[1][name]', value: 'Banana'},
{name: 'fruits[1][id]', value: '1'},
{name: 'fruits[2][name]', value: 'Strawberry'},
{name: 'fruits[2][id]', value: '2'},
{name: 'fruits[3][name]', value: 'Raspberry'},
{name: 'fruits[3][id]', value: '3'},
{name: 'fruits[4][name]', value: 'Kiwi'},
{name: 'fruits[4][id]', value: '4'},
{name: 'fruits[5][name]', value: 'Mango'},
{name: 'fruits[5][id]', value: '5'},
{name: 'selected_fruit_id', value: '1'},
]
// This variable holds the result
var obj = {}
serializedInputs.forEach(function(item) {
// Turning square brackets into dot notation
setObj(item.name.replace(/\]/g, '').replace(/\[/g, '.'), item.value, obj);
})
Result
{
"fruits": {
"1": {
"name": "Banana",
"id": "1"
},
"2": {
"name": "Strawberry",
"id": "2"
},
"3": {
"name": "Raspberry",
"id": "3"
},
"4": {
"name": "Kiwi",
"id": "4"
},
"5": {
"name": "Mango",
"id": "5"
}
},
"selected_fruit_id": "1"
}
How about this?
It'll create or copy/overwrite an existing object.
function expando(obj, base) {
return Object.keys(obj)
.reduce((clone, key) => {
key.split('.').reduce((innerObj, innerKey, i, arr) =>
innerObj[innerKey] = (i+1 === arr.length) ? obj[key] : innerObj[innerKey] || {}, clone)
return clone;
}, Object.assign({}, base));
}
console.log(expando({'a.b': 1})) // { a: { b : 1 }}
console.log(expando({'b.c': 2}, { a: 1 })) // { a: 1, b: { c: 2 }}
console.log(expando({'b.c.d': 2, 'e.f': 3}, { a: 1 })) // { a: 1, b: { c: { d: 2 } }, e: { f: 3}}
NOTE: ES6 needed for arrow functions and Object.assign()
.
Feel free to play around:
https://jsbin.com/setazahuce/1/edit?js,console
I believe dojo's setObject does what you want. If you (understandably) don't want to pull in all of dojo then I would recommend examining their (freely available) source or loading just base (only 4k) via AMD. It looks something like this :
function setObject(name, value, context) {
var parts=name.split("."),
p=parts.pop();
for(var i=0, j; context && (j=parts[i]); i++){
context = (j in context ? context[j] : context[j]={});
}
return context && p ? (context[p]=value) : undefined; // Object
}
So in your case you would do :
x={a:1,b:2};
setObject("c.d", 3, x);
Warning : unless you only ever deal with trivial cases, I would urge you to still go check out the full dojo implementation, which deals with cases where no context is provided etc.
Off the top of my head I guess you can do something like this:
function addValueToObj(obj, newProp) {
newProp = newProp.split("="); // separate the "path" from the "value"
var path = newProp[0].split("."), // separate each step in the "path"
val = newProp.slice(1).join("="); // allow for "=" in "value"
for (var i = 0, tmp = obj; i < path.length - 1; i++) {
tmp = tmp[path[i]] = {}; // loop through each part of the path adding to obj
}
tmp[path[i]] = val; // at the end of the chain add the value in
}
var x = {a:1, b:2};
addValueToObj(x, "c.d=3");
// x is now {"a":1,"b":2,"c":{"d":"3"}}
addValueToObj(x, "e.f.g.h=9=9");
// x is now {"a":1,"b":2,"c":{"d":"3"},"e":{"f":{"g":{"h":"9=9"}}}}
Demo: http://jsfiddle.net/E8dMF/1/
You can do this with lodash.set()
> l=require('lodash') > x={a:1,b:2}; { a: 1, b: 2 } > l.set(x, 'c.d', 3) { a: 1, b: 2, c: { d: 3 } }
Here's a heavily commented version that should be somewhat straightforward to understand.
// stores the configured data
configStore = {};
config = {
set: function(keyValueString) {
// Split the string by the =
var pair = keyValueString.split('=');
// left of the = is the key path
var keyPath = pair[0];
// right of the = is the value to set
var value = pair[1];
// split keyPath into an array of keys
var keys = keyPath.split('.');
var key; // used in loop
// the current level of object we are drilling into.
// Starts as the main root config object.
var currentObj = configStore;
// Loop through all keys in the key path, except the last one (note the -1).
// This creates the object structure implied by the key path.
// We want to do something different on the last iteration.
for (var i=0; i < keys.length-1; i++) {
// Get the current key we are looping
key = keys[i];
// If the requested level on the current object doesn't exist,
// make a blank object.
if (typeof currentObj[key] === 'undefined') {
currentObj[key] = {};
}
// Set the current object to the next level of the keypath,
// allowing us to drill in.
currentObj = currentObj[key];
}
// Our loop doesn't handle the last key, because that's when we
// want to set the actual value. So find the last key in the path.
var lastKey = keys[keys.length-1]
// Set the property of the deepest object to the value.
currentObj[lastKey] = value;
}
};
// Do it.
config.set('omg.wtf.bbq=123')
// Check it.
alert(configStore.omg.wtf.bbq); // 123