anni
anni

Reputation: 288

retrieving the value of object using strings

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

Upvotes: 1

Views: 362

Answers (2)

user663031
user663031

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

jfriend00
jfriend00

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

Related Questions