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

2
  • 1
    Your code doesn't work because you set obj to obj[0], which is the string "obj", not the variable obj. Commented Jul 21, 2014 at 4:08
  • but obj is a array initally Commented Jul 21, 2014 at 4:12

2 Answers 2

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/

Sign up to request clarification or add additional context in comments.

5 Comments

@anni - I added a cleaned up version.
This code is not robust in the face of missing properties.
@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).
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').
@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.
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.

2 Comments

Nice use of .reduce() as long as you don't want IE8 support or you want to add a polyfill.
You have syntax errors in passing the callback to .reduce(). Missing the keyword function(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.