1

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?

JSFIDDLE

thanks in advance

Community
  • 1
  • 1
anni
  • 290
  • 1
  • 3
  • 15

2 Answers2

1

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/

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • @anni - I added a cleaned up version. – jfriend00 Jul 21 '14 at 04:22
  • This code is not robust in the face of missing properties. –  Jul 21 '14 at 04:24
  • @torazaburo - Yes, and what would you expect it to return in that case? In regular JS code, asking for a multi-level property when one of the higher level properties doesn't exist is a run-time error which is also what would happen here. The code can obviously be modified to return `undefined` or whatever other behavior the OP wants, but no behavior like that was specified or asked for so I'm unsure what the OP wants (if anything) in that regard). – jfriend00 Jul 21 '14 at 04:25
  • A run-time error is certainly one possible definition of the behavior, but returning `undefined` seems slightly better and is what I am used to in environments like Ember, when doing `Ember.get(obj, 'foo.bar')`. –  Jul 21 '14 at 04:27
  • @anni - I added optional `baseObj` argument which can be ignored if your object is at the top level scope, but it can be passed if you want to reference the path relative to some other object besides the global object. – jfriend00 Jul 21 '14 at 04:44
0

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.