Reputation: 288
Here I am in a situation where I have to work only with strings, and because of this I also have to retrieve the value of an object from strings, in short:
to retrieve the value from an object we write:
someObject.property1.name // for say
but in my case i want to retrieve value from an object using string, i.e
'someObject.property1.name' // for say
since I was not so confident that I could do this, so I preferred tho search on internet and the most suitable solution which I got was
#1
Object.byString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
while (a.length) {
var n = a.shift();
if (n in o) {
o = o[n];
} else {
return;
}
}
return o;
}
from here
#2
var deep_value = function(obj, path){
for (var i=0, path=path.split('.'), len=path.length; i<len; i++){
obj = obj[path[i]];
};
return obj;
};
from here
but as I said they are the most suitable example because they all are taking one extra parameter i.e. obj
, O
and so on... which is creating trouble for me, so I tried to improve the above code in search 2 because it is compact, and that results in failure. That code is:
var obj = {
foo: { bar: 'baz' }
};
var deep_value = function(path){
var obj = path.split('.');
obj = obj[0];
for (var i=0, path=path.split('.'), len=path.length; i<len; i++){
obj = obj[path[i+1]];
};
return obj;
};
alert(deep_value('obj.foo.bar'));
(I edited in his code for just an experiment). the above code does not need obj which is a perfect code - if it worked - and don't see any mistake, then why this code is not working, what is the correct code?
thanks in advance
Upvotes: 1
Views: 362
Reputation:
This be expressed compactly using reduce
:
function eval_dotted_path(path) {
return path.split('.').reduce(function(value, segment) {
return value && value[segment];
}, window);
}
This splits up the path into "segments" on the dots, then calls reduce
to "loop" over the segments, finding the next value inside the object on each iteration. The first segment is found in the global namespace, aka "window".
The value && value[segment]
ensures that if at any point the current value is null/undefined (because the segment property was not present), undefined/null is returned.
If instead you want to find the value indicated by the dotted path starting from a known object, you can tweak this as
function eval_dotted_path_from_object(object, path) {
return path.split('.').reduce(function(value, segment) {
return value && value[segment];
}, object);
}
after which you can redefine the initial function as
function eval_dotted_path(path) {
return eval_dotted_path_from_object(window, path);
}
If you're in an old environment without Array.prototype.reduce
, consider using Underscore's _.reduce
, or a polyfill; some frameworks may provide the polyfill for you.
Upvotes: 0
Reputation: 707258
There were a couple problems with your #3 option:
First obj = obj[0];
was just going to have obj === "obj"
which isn't going to help you at all. You need to actually get window["obj"]
to get the top level object.
Second, you were traversing the for
loop one too many times and going off the end of the path
array.
Making changes in both these areas will make it work if obj
is at the top level scope:
var obj = {
foo: { bar: 'baz' }
};
var deep_value = function(path, baseObj){
baseObj = baseObj || window;
var obj = path.split('.');
obj = baseObj[obj[0]];
for (var i=1, path=path.split('.'), len=path.length; i<len; i++){
obj = obj[path[i]];
};
return obj;
};
alert(deep_value('obj.foo.bar'));
Working demo: http://jsfiddle.net/jfriend00/jGb5p/
Here's a bit of a cleaned-up version:
var obj = {
foo: { bar: 'baz' }
};
var deep_value = function(path, baseObj){
baseObj = baseObj || window;
var pieces = path.split('.');
// get root level object
var obj = baseObj[pieces[0]];
for (var i = 1, len = pieces.length; i < len; i++){
obj = obj[pieces[i]];
}
return obj;
};
console.log(deep_value('obj.foo.bar'));
Working demo: http://jsfiddle.net/jfriend00/7J4Jb/
Upvotes: 1