I have a nested Javascript object like
var data = { 'name': { 'heading': 'Name', 'required': 1, 'type': 'String' },
'profile': {
'age': { 'heading': 'Age', 'required': 0, 'type': 'Number' },
'phone': { 'heading': 'Phone', 'required': 0, 'type': 'String'},
'city': { 'heading': 'City', 'required': 0, 'type': 'String'},
},
'status': { 'heading': 'Status', 'required': 1, 'type': 'String' }
};
Here, I can access the fields as data.profile.age.type or data.name.type. No Issues
And if I have dynamic variable names, I can access as below. Again, No Problems.
f = 'profile'; data[f].age.type
But, here I have variable names like 'name', 'profile.age', 'profile.city' etc and obviously I cannot access them as f = 'profile.age'; data[f].type which will not work.
Can anyone guide me how to access them (get/set) in the most straight-forward and simple way?
Note: I tried this and it works for get.
data.get = function(p) { o = this; return eval('o.'+p); };
f = 'profile.age'; data.get(f).name;
though set does not seem to be simple enough. Please let me know, if there are better solutions for get and set as well.
Don't use eval unless absolutely necessary. :) At least in this case, there are better ways to do it -- you can split the nested name into individual parts and iterate over them:
data.get = function(p) {
var obj = this;
p = p.split('.');
for (var i = 0, len = p.length; i < len - 1; i++)
obj = obj[p[i]];
return obj[p[len - 1]];
};
data.set = function(p, value) {
var obj = this;
p = p.split('.');
for (var i = 0, len = p.length; i < len - 1; i++)
obj = obj[p[i]];
obj[p[len - 1]] = value;
};
You can just nest the brackets:
var a = 'name', b = 'heading';
data[a][b]; // = `Name`
Perhaps a function that takes in the path to the property you're interested in and breaks it up into tokens representing properties. Something like this (this is very rough, of course):
data.get = function(path) {
var tokens = path.split('.'), val = this[tokens[0]];
if (tokens.length < 2) return val;
for(var i = 1; i < tokens.length; i++) {
val = val[tokens[i]];
}
return val;
}
example:
var f = 'one.two';
var data = { one: {two:'hello'}};
data.get = /* same as above */;
var val = data.get(f);
A clean way to access/set nested values is using reduce in ES6:
const x = ['a', 'b', 'c'], o = {a: {b: {c: 'tigerking'}}}
// Get value: "tigerking"
console.log(x.reduce((a, b) => a[b], o))
// Set value:
x.slice(0, x.length-1).reduce((a, b) => a[b], o)[x[x.length-1]] = 'blossom'
console.log(o) // {a: {b: {c: 'blossom'}}}
So, you can first convert your variable 'profile.age' to an array using 'profile.age'.split('.'), then use the approach above.
This uses the jquery.each() function to traverse down a json tree using a string variable that may or may not contain one or more "."'s. You could alternatively pass in an array and omit the .split();
pathString = something like "person.name"
jsonObj = something like {"person" : {"name":"valerie"}}.
function getGrandchild(jsonObj, pathString){
var pathArray = pathString.split(".");
var grandchild = jsonObj;
$.each(pathArray, function(i, item){
grandchild = grandchild[item];
});
return grandchild;
};
returns "valerie"
Related
I am working on some software that reads/writes information in localStorage using a handler. You can find a working example here: http://jsbin.com/wifucugoko/edit?js,console
My problem is with the segment of code below (focusing on the switch statement):
_t.set = function(path, value) { // Update a single value or object
if (~path.indexOf(".")) {
let o = path.split(".")[0],
p = this.get(o),
q = path.split(".").slice(1);
switch (q.length) {
// There has to be a better way to do this...
case 1:
p[q[0]] = value;
break;
case 2:
p[q[0]][q[1]] = value;
break;
case 3:
p[q[0]][q[1]][q[2]] = value;
break;
case 4:
p[q[0]][q[1]][q[2]][q[3]] = value;
break;
case 5:
p[q[0]][q[1]][q[2]][q[3]][q[4]] = value;
break;
case 6:
p[q[0]][q[1]][q[2]][q[3]][q[4]][q[5]] = value;
break;
default:
return "error";
}
b.setItem(o, JSON.stringify(p));
return p;
} else {
b.setItem(path, JSON.stringify(value));
return this.get(path);
}
};
I am not going to be the only one using this codebase, and I am trying to make it easy for others to update any value that could be placed in localStorage. Right now you can update a value by using something like local.set('item.subitem.proeprty', 'value') Though the code above does that, it's ugly and doesn't scale.
How can this method be improved to (1) update a property nested at any depth automatically, instead of writing an infinitely-long switch statement, and (2) not lace a parent object with [object Object] after a value is updated?
This question has nothing to do with my use of localStorage. I originally posted this question in code review, which requires a working contextual example. They closed this question immediately, since part of my problem is the code I provided doesn't work once you start dealing with updating a value nested more than six objects deep. Though I could have continued my switch statement indefinitely, that's exactly what I'm trying to avoid.
With the three examples provided you'll see that setting a value in one place doesn't remove values in other places:
local.set('user.session.timeout', false);
local.set('user.name', {first:'john', last:'doe', mi:'c'});
local.set('user.PIN', 8675309);
All these values, though set at different times, only UPDATE or create a value, they do NOT clear any pre-existing values elsewhere.
As for me the minimal optimization would be following:
if (~path.indexOf(".")) {
let o = path.split(".")[0],
p = this.get(o),
q = path.split(".").slice(1),
dist = p;
q.forEach(function(item, index) {
if (index < q.length - 1) {
dist = dist[item];
} else {
dist[item] = value;
}
});
b.setItem(o, JSON.stringify(p));
return p;
} else {
changed parts:
dist variable is created
hardcoded switch is replaced with foreach
You could try something like this, if the path does not exists, the value is null:
function retreiveValueFromObject(path, object) {
var pathList = path.split(".");
var currentValue = object;
var brokeEarly = false;
for (var i = 0; i < pathList.length; i++) {
if (currentValue[pathList[i]]) {
currentValue = currentValue[pathList[i]];
} else {
brokeEarly = true;
break;
}
}
return {
value: currentValue,
brokeEarly: brokeEarly
};
}
function setValueInObject(path, value, object) {
var nestedObj = retreiveValueFromObject(path, object).value;
var pathList = path.split(".");
var finalKey = pathList[pathList.length - 1];
nestedObj[finalKey] = value;
}
var someObject = {
a: {
c: {
d: "value"
},
z: "c"
},
b: {
f: {
x: "world"
},
g: "hello"
},
};
console.log(retreiveValueFromObject("b.f.x", someObject));
setValueInObject("b.f.y", "newValue", someObject);
console.log(someObject);
What you are looking for is a little bit of recursion, I just implemented the update method.
let localStorageHandler = function() {
let b = window.localStorage,
_t = this;
_t.get = function(a) {
try {
return JSON.parse(b.getItem(a))
} catch (c) {
return b.getItem(a)
}
};
function descendAndUpdate(obj, path, value) {
let current = path[0],
remainingPath = path.slice(1);
// found and update
if (obj.hasOwnProperty(current) && remainingPath.length === 0) {
obj[current] = value;
// found but still not there
} else if (obj.hasOwnProperty(current)) {
return descendAndUpdate(obj[current], remainingPath, value);
}
// if you want do add new properties use:
// obj[current] = value;
// in the else clause
else {
throw('can not update unknown property');
}
}
_t.set = function(path, value) { // Update a single value or object
if (~path.indexOf(".")) {
let o = path.split(".")[0],
p = this.get(o),
q = path.split(".").slice(1);
descendAndUpdate(p, q, value);
console.log(p);
b.setItem(o, JSON.stringify(p));
return p;
} else {
b.setItem(path, JSON.stringify(value));
return this.get(path);
}
};
_t.remove = function(a) { // removes a single object from localstorage
let c = !1;
a = "number" === typeof a ? this.key(a) : a;
a in b && (c = !0, b.removeItem(a));
return c
};
};
let local = new localStorageHandler();
// Create user and session info if it doesn't exist
let blankUser = new Object({
alias: '',
dob: '',
PIN: '',
level: 0,
name: {
first: '',
last: '',
mi:'',
},
session: {
token: '',
timeout: true,
lastChange: Date.now()
}
});
local.remove('user');
// Loads user data into localstorage
if (!local.get('user')) {
local.set('user', blankUser);
}
local.set('user.session.timeout', false);
local.set('user.name', {first:'john', last:'doe', mi:'c'});
local.set('user.PIN', 8675309);
// new property
// local.set('user.sunshine', { 'like': 'always' });
console.log(local.get('user'));
A friend of mine would always prefer stacks over recursion, which would be a second option. Anyway I agree with many of the comments here. You already know your domain model. Unless you have a very good reason for this approach spend more time on serializing and unserializing those objects in the database. I have the impression you would be able to work with your data in a more natural way because the aspect of updating fields in a database would be abstracted away.
I am working on a similar project at the moment. What I am doing is storing the data in something I called a WordMatrix (https://github.com/SamM/Rephrase/blob/master/WordMatrix.js), maybe you could use something like it in your solution.
My project is a WIP but the next step is literally to add support for localStorage. The project itself is a database editor that works with key => value stores. You can view the prototype for it here: (https://samm.github.io/Rephrase/editor.html)
Once I have implemented the localStorage aspect I will update this post.
Your topic reminds me one recent another topic.
Trying to enhance the answer I provided, I propose you these functions:
// Function to get a nested element:
function obj_get_path(obj, path) {
return path.split('.').reduce((accu, val) => accu[val] || 'Not found', obj);
}
// Function to set a nested element:
function obj_set_path(obj, path, value) {
var result = obj;
var paths = path.split('.');
var len = paths.length;
for (var i = 0; i < len - 1; i++) {
result = result[paths[i]] || {};
}
result[paths[len - 1]] = value;
return obj;
}
// Example object
var obj = {
name0: 'A name',
level0: {
name1: 'An other name',
level1: {
level2: {
name3: 'Name to be changed',
text3: 'Some other text'
}
}
}
}
// Use of the function
obj = obj_set_path(obj, 'level0.level1.level2.name3', 'Takit Isy');
obj = obj_set_path(obj, 'level0.level1.level2.new3', 'I’m a new element!');
var obj_level2 = obj_get_path(obj, 'level0.level1.level2');
// Consoling
console.log('Consoling of obj_level2:\n', obj_level2);
console.log('\nConsoling of full obj:\n', obj); // To see that the object is correct
⋅
⋅
⋅
We could also adapt the 2nd function in my above snippet so that it works for both get and set, depending of if "value" is set:
// We could also adapt the second function for both uses:
function obj_path(obj, path, value = null) {
var result = obj;
var paths = path.split('.');
var len = paths.length;
for (var i = 0; i < len - 1; i++) {
result = result[paths[i]] || {};
}
// Return result if there is no set value
if (value === null) return result[paths[len - 1]];
// Set value and return
result[paths[len - 1]] = value;
return obj;
}
// Example object
var obj = {
name0: 'A name',
level0: {
name1: 'An other name',
level1: {
level2: {
name3: 'Name to be changed',
text3: 'Some other text'
}
}
}
}
// Use of the function
obj = obj_path(obj, 'level0.level1.level2.name3', 'Takit Isy');
obj = obj_path(obj, 'level0.level1.level2.new3', 'I’m a new element!');
var obj_level2 = obj_path(obj, 'level0.level1.level2');
// Consoling
console.log('Consoling of obj_level2:\n', obj_level2);
console.log('\nConsoling of full obj:\n', obj); // To see that the object is correct
Hope it helps.
How about:
function parse(str) {
var arr = str.split('.');
return function(obj) {
return arr.reduce((o, i) => o[i], obj);
}
}
let foo = {
a: {
b: {
c: {
bar: 0
}
}
}
}
let c = parse('a.b.c')(foo);
console.log(c.bar);
c['bar'] = 1;
console.log(foo);
My application creates a JavaScript object, like the following:
myObj= {1:[Array-Data], 2:[Array-Data]}
But I need this object as an array.
array[1]:[Array-Data]
array[2]:[Array-Data]
So I tried to convert this object to an array by iterating with $.each through the object and adding the element to an array:
x=[]
$.each(myObj, function(i,n) {
x.push(n);});
Is there an better way to convert an object to an array or maybe a function?
If you are looking for a functional approach:
var obj = {1: 11, 2: 22};
var arr = Object.keys(obj).map(function (key) { return obj[key]; });
Results in:
[11, 22]
The same with an ES6 arrow function:
Object.keys(obj).map(key => obj[key])
With ES7 you will be able to use Object.values instead (more information):
var arr = Object.values(obj);
Or if you are already using Underscore/Lo-Dash:
var arr = _.values(obj)
var myObj = {
1: [1, 2, 3],
2: [4, 5, 6]
};
var array = $.map(myObj, function(value, index) {
return [value];
});
console.log(array);
Output:
[[1, 2, 3], [4, 5, 6]]
Simply do
Object.values(obj);
That's all!
I think you can use for in but checking if the property is not inerithed
myObj= {1:[Array-Data], 2:[Array-Data]}
var arr =[];
for( var i in myObj ) {
if (myObj.hasOwnProperty(i)){
arr.push(myObj[i]);
}
}
EDIT - if you want you could also keep the indexes of your object, but you have to check if they are numeric (and you get undefined values for missing indexes:
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
myObj= {1:[1,2], 2:[3,4]}
var arr =[];
for( var i in myObj ) {
if (myObj.hasOwnProperty(i)){
if (isNumber(i)){
arr[i] = myObj[i];
}else{
arr.push(myObj[i]);
}
}
}
If you know the maximum index in you object you can do the following:
var myObj = {
1: ['c', 'd'],
2: ['a', 'b']
},
myArr;
myObj.length = 3; //max index + 1
myArr = Array.prototype.slice.apply(myObj);
console.log(myArr); //[undefined, ['c', 'd'], ['a', 'b']]
Since ES5 Object.keys() returns an array containing the properties defined directly on an object (excluding properties defined in the prototype chain):
Object.keys(yourObject).map(function(key){ return yourObject[key] });
ES6 takes it one step further with arrow functions:
Object.keys(yourObject).map(key => yourObject[key]);
Nowadays, there is a simple way to do this : Object.values().
var myObj = {
1: [1, 2, 3],
2: [4, 5, 6]
};
console.log(Object.values(myObj));
Output:
[[1, 2, 3], [4, 5, 6]]
This doesn't required jQuery, it's been defined in ECMAScript 2017.
It's supported by every modern browser (forget IE).
The best method would be using a javascript -only function:
var myArr = Array.prototype.slice.call(myObj, 0);
x = [];
for( var i in myObj ) {
x[i] = myObj[i];
}
ECMASCRIPT 5:
Object.keys(myObj).map(function(x) { return myObj[x]; })
ECMASCRIPT 2015 or ES6:
Object.keys(myObj).map(x => myObj[x])
How about jQuery.makeArray(obj)
This is how I did it in my app.
ES8 way made easy:
The official documentation
const obj = { x: 'xxx', y: 1 };
let arr = Object.values(obj); // ['xxx', 1]
console.log(arr);
The solving is very simple
var my_obj = {1:[Array-Data], 2:[Array-Data]}
Object.keys(my_obj).map(function(property_name){
return my_obj[property_name];
});
Fiddle Demo
Extension to answer of bjornd .
var myObj = {
1: [1, [2], 3],
2: [4, 5, [6]]
}, count = 0,
i;
//count the JavaScript object length supporting IE < 9 also
for (i in myObj) {
if (myObj.hasOwnProperty(i)) {
count++;
}
}
//count = Object.keys(myObj).length;// but not support IE < 9
myObj.length = count + 1; //max index + 1
myArr = Array.prototype.slice.apply(myObj);
console.log(myArr);
Reference
Array.prototype.slice()
Function.prototype.apply()
Object.prototype.hasOwnProperty()
Object.keys()
If you want to keep the name of the object's properties as values. Example:
var fields = {
Name: { type: 'string', maxLength: 50 },
Age: { type: 'number', minValue: 0 }
}
Use Object.keys(), Array.map() and Object.assign():
var columns = Object.keys( fields ).map( p => Object.assign( fields[p], {field:p} ) )
Result:
[ { field: 'Name', type: 'string', maxLength: 50 },
{ field: 'Age', type: 'number', minValue: 0 } ]
Explanation:
Object.keys() enumerates all the properties of the source ; .map() applies the => function to each property and returns an Array ; Object.assign() merges name and value for each property.
I made a custom function:
Object.prototype.toArray=function(){
var arr=new Array();
for( var i in this ) {
if (this.hasOwnProperty(i)){
arr.push(this[i]);
}
}
return arr;
};
After some tests, here is a general object to array function convertor:
You have the object:
var obj = {
some_key_1: "some_value_1"
some_key_2: "some_value_2"
};
The function:
function ObjectToArray(o)
{
var k = Object.getOwnPropertyNames(o);
var v = Object.values(o);
var c = function(l)
{
this.k = [];
this.v = [];
this.length = l;
};
var r = new c(k.length);
for (var i = 0; i < k.length; i++)
{
r.k[i] = k[i];
r.v[i] = v[i];
}
return r;
}
Function Use:
var arr = ObjectToArray(obj);
You Get:
arr {
key: [
"some_key_1",
"some_key_2"
],
value: [
"some_value_1",
"some_value_2"
],
length: 2
}
So then you can reach all keys & values like:
for (var i = 0; i < arr.length; i++)
{
console.log(arr.key[i] + " = " + arr.value[i]);
}
Result in console:
some_key_1 = some_value_1
some_key_2 = some_value_2
Edit:
Or in prototype form:
Object.prototype.objectToArray = function()
{
if (
typeof this != 'object' ||
typeof this.length != "undefined"
) {
return false;
}
var k = Object.getOwnPropertyNames(this);
var v = Object.values(this);
var c = function(l)
{
this.k = [];
this.v = [];
this.length = l;
};
var r = new c(k.length);
for (var i = 0; i < k.length; i++)
{
r.k[i] = k[i];
r.v[i] = v[i];
}
return r;
};
And then use like:
console.log(obj.objectToArray);
You can create a simple function to do the conversion from object to array, something like this can do the job for you using pure javascript:
var objectToArray = function(obj) {
var arr = [];
if ('object' !== typeof obj || 'undefined' === typeof obj || Array.isArray(obj)) {
return obj;
} else {
Object.keys(obj).map(x=>arr.push(obj[x]));
}
return arr;
};
or this one:
var objectToArray = function(obj) {
var arr =[];
for(let o in obj) {
if (obj.hasOwnProperty(o)) {
arr.push(obj[o]);
}
}
return arr;
};
and call and use the function as below:
var obj = {1:'a', 2:'b', 3:'c', 4:'d', 5:'e'};
objectToArray(obj); // return ["a", "b", "c", "d", "e"]
Also in the future we will have something called Object.values(obj), similar to Object.keys(obj) which will return all properties for you as an array, but not supported in many browsers yet...
I have the following code (which works correctly):
var arr = [];
for(var i = 0; i < 2; i++) {
var obj = { a: 'A'};
obj.c = 'C' + i;
arr.push(obj);
}
// now arr is:
// [ {a: 'A', 'c': 'C0'}, {a: 'A', 'c': 'C1'} ]
To improve the performance of my code, I placed the obj outside the loop, then adding/modifying the new property only, like this:
var arr = [];
var obj = { a: 'A'};
for(var i = 0; i < 2; i++) {
obj.c = 'C' + i;
arr.push(obj);
}
// now arr is:
// [ {a: 'A', 'c': 'C1'}, {a: 'A', 'c': 'C1'} ]
Why both objects got C1 ? Please explain what I'm doing wrong, and how to place the object out of the loop and get correct results?
Note: I know this is a simple problem where performance is not an issue, but I'm actually dealing with big number of objects in reality where performance matters.
You are pushing the object (not a copy of the object) to the array, and then changing it.
If you want different objects in each index, then you need to create a new object each time you go around the loop.
Like the others wrote, you are changing the same object (the original) all the time which ends in the results being the same (the original).
To place the object out of the loop and still get the correct result, you would still have to 'copy' it inside of the loop:
var arr = [];
var obj = { a: 'A'};
var objstring = JSON.stringify(obj);
for(var i = 0; i < 2; i++) {
var obj = JSON.parse(objstring);
obj.c = 'C' + i;
arr.push(obj);
}
This question already has answers here:
indexOf method in an object array?
(29 answers)
Closed 9 years ago.
Error in following code:-
var x = [{id: 'abc'}, {id: 'xyz'}];
var index = x.indexOf({id: 'abc'});
What's the syntax for above?
You should pass reference to exactly the same object you have defined in the array:
var a = {id: 'abc'},
b = {id: 'xyz'};
var index = [a, b].indexOf(a); // 0
Objects are only equal to each other if they refer to the exact same instance of the object.
You would need to implement your own search feature. For example:
Array.prototype.indexOfObject = function(obj) {
var l = this.length, i, k, ok;
for( i=0; i<l; i++) {
ok = true;
for( k in obj) if( obj.hasOwnProperty(k)) {
if( this[i][k] !== obj[k]) {
ok = false;
break;
}
}
if( ok) return i;
}
return -1; // no match
};
var x = [{id: 'abc'}, {id: 'xyz'}];
var index = x.indexOfObject({id: 'abc'}); // 0
Iterate through the array like this:
for(var i = 0, len = x.length; i < len; i++) {
if (x[i].id === 'abc') {
console.log(i);
break;
}
}
Otherwise, you'll have to make sure the pointers are the same for the objects you're trying to look for with indexOf
Let's have some nice code here ;)
Underscore.js provides where, which is also fairly easy to write in pure JS:
Array.prototype.where = function(props) {
return this.filter(function(e) {
for (var p in props)
if (e[p] !== props[p])
return false;
return true;
});
}
Another (more flexible) function understands either an object or a function as a selector:
Array.prototype.indexBy = function(selector) {
var fn = typeof selector == "function" ? selector :
function(elem) {
return Object.keys(selector).every(function(k) {
return elem[k] === selector[k]
})
}
return this.map(fn).indexOf(true);
}
and then
var x = [{id: 'abc'}, {id: 'xyz'}];
x.indexBy({'id': 'xyz'}) // works
x.indexBy(function(elem) { return elem.id == 'xyz' }) // works too
var o = {}
var x = [o]
console.log(x.indexOf(o))
With x.indexOf({}) you create a new Object the is not present in the array
The following is the most wonderful method:-
var indexId = x.map(function(e) { return e.id; }).indexOf('abc');
as seen in this answer
I ran into a situation where I need access to a javascript object from the server. The server returns the string name of the function or object and based on other metadata I will evaluate the object differently.
Originally I was evaluating (eval([string])) and everything was fine. Recently I was updating the function to not use eval for security peace of mind, and I ran into an issue with namespaced objects/functions.
Specifically I tried to replace an eval([name]) with a window[name] to access the object via the square bracket syntax from the global object vs eval.
But I ran into a problem with namespaced objects, for example:
var strObjName = 'namespace.serviceArea.function';
// if I do
var obj = eval(strObjName); // works
// but if I do
var obj = window[strObjName]; // doesn't work
Can anyone come up with a good solution to avoid the use of eval with namespaced strings?
You could split on . and resolve each property in turn. This will be fine so long as none of the property names in the string contain a . character:
var strObjName = 'namespace.serviceArea.function';
var parts = strObjName.split(".");
for (var i = 0, len = parts.length, obj = window; i < len; ++i) {
obj = obj[parts[i]];
}
alert(obj);
Just thought I'd share this because I made this the other day. I didn't even realize reduce was available in JS!
function getByNameSpace(namespace, obj) {
return namespace.split('.').reduce(function(a,b) {
if(typeof a == 'object') return a[b];
else return obj[a][b];
});
}
Hope someone finds this useful..
I came up with this:
function reval(str){
var str=str.split("."),obj=window;
for(var z=0;z<str.length;z++){
obj=obj[str[z]];
}
return obj;
}
eval("window.this.that");
reval("this.that"); //would return the object
reval("this.that")(); //Would execute
I have written a different solution, using recursion. I was using this to namespace the name attribute of input elements. Take, for example, the following:
<input type="number" name="testGroup.person.age" />
And let's say we set the value of that input to "25". We also have an object in our code somewhere:
var data = {
testGroup: {
person: {
name: null,
age: null,
salary: null
}
}
};
So the goal here is to automatically put the value in our input element into the correct position in that data structure.
I've written this little function to create a nested object based on whatever array of namespaces you pass to it:
var buildObject = function ( obj, namespaceArray, pos ) {
var output = {};
output[namespaceArray[pos]] = obj;
if ( pos > 0 ) {
output = buildObject(output, namespaceArray, pos-1);
}
return output;
};
How does it work? It starts at the end of the namespaceArray array, and creates an object with one property, the name of which is whatever is in the last slot in namespaceArray, and the value of which is obj. It then recurses, wrapping that object in additional objects until it runs out of names in namespaceArray. pos is the length of the namespaceArray array. You could just work it out in the first function call, something like if ( typeof(pos) === "undefined" ) pos = namespaceArray.length - 1, but then you'd have an extra statement to evaluate every time the function recursed.
First, we split the name attribute of the input into an array around the namespace separators and get the value of the input:
var namespaceArray = inputElement.name.split("."),
obj = inputElement.value;
// Usually you'll want to do some undefined checks and whatnot as well
Then we just call our function and assign the result to some variable:
var myObj = buildObject(obj, namespaceArray, namespaceArray.length - 1);
myObj will now look like this:
{
testGroup: {
person: {
age: 25
}
}
}
At this point I use jQuery's extend function to merge that structure back into the original data object:
data = $.extend(true, data, myObj);
However, merging two objects is not very difficult to do in framework-free JavaScript and there is plenty of existing code that gets the job done well.
I'm sure there are more efficient ways to get this done, but this method meets my needs well.
I know this has been answered satisfactory to the asker, but I recently had this issue, but with WRITING to the value. I came up with my own static class to handle reading and writing based on a string path. The default path separator is . but you can modify it to be anything, such as /. The code is also commented incase you wonder how it works.
(function(){
var o = {}, c = window.Configure = {}, seperator = '.';
c.write = function(p, d)
{
// Split the path to an array and assaign the object
// to a local variable
var ps = p.split(seperator), 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(seperator), co = o;
for(var i = 0; i < ps.length; i++)
{
co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
}
return co;
}
})();
My sample on pastebin:
http://pastebin.com/13xUnuyV
i liked you code, and i did a lot of similar stuff using PHP.
But here that was tough as i though...
So i used answers #LordZardeck (https://stackoverflow.com/a/9338381/1181479) and #Alnitak (https://stackoverflow.com/a/6491621/1181479).
And here what i have, just use it:
var someObject = {
'part1' : {
'name': 'Part 1',
'size': '20',
'qty' : '50'
},
'part2' : {
'name': 'Part 2',
'size': '15',
'qty' : '60'
},
'part3' : [
{
'name': 'Part 3A РУКУ!!!',
'size': '10',
'qty' : '20'
}, {
'name': 'Part 3B',
'size': '5',
'qty' : '20'
}, {
'name': 'Part 3C',
'size': '7.5',
'qty' : '20'
}
]
};
//var o = {}, c = window.Configure = {}, seperator = '.';
var c = function(){
this.o = {};
this.seperator = ".";
this.set = function(obj){
this.o = obj;
}
this.write = function(p, d) {
p = p.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
p = p.replace(/^\./, ''); // strip leading dot
// Split the path to an array and assaign the object
// to a local variable
var ps = p.split(this.seperator), co = this.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;
}
this.read = function(p) {
p = p.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
p = p.replace(/^\./, ''); // strip leading dot
var ps = p.split(this.seperator), co = this.o;
/*
for(var i = 0; i < ps.length; i++)
{
co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
}
*/
while (ps.length) {
var n = ps.shift();
if (n in co) {
co = co[n];
} else {
return;
}
}
return co;
}
};
var n = new c();
n.set(someObject);
console.log('whas');
console.log('n.read part.name', n.read('part1.name'));
n.write('part3[0].name', "custom var");
console.log('part1.name now changed');
n.write('part1.name', "tmp");
console.log('n.read part.name', n.read('part1.name'));
console.log('----');
console.log('before', someObject);
console.log('someObject.part1.name', someObject.part1.name);
console.log('someObject.part3[0].name', someObject.part3[0].name);
It is possible to do this using functional programming techniques such as
(function (s) {return s.split('.').reduce(function(p,n) { p[n] = p[n] || {}; return p[n];},window);})("some.cool.namespace");
This can be assigned to a global function for re-use
window.ns = (function (s) {return s.split('.').reduce(function(p,n) { p[n] = p[n] || {}; return p[n];},window);})
Then we can do the following
ns("some.cool").namespace = 5;
if (5 != ns("some.cool.namespace")) { throw "This error can never happen" }
The following assumes that the parts of strObjName are separated by . and loops through starting at window until it gets down to the function you want:
var strObjParts = strObjName.split('.');
var obj = window;
for(var i in strObjParts) {
obj = obj[strObjParts[i]];
}