3

If I have an object like this...

var rooter = {
    name: 'Billy',
    lastName: 'Moon',
    height: '1.4m'
}

I can access the properties from variables like this...

var name = "name"
console.log(rooter[name])

var arb = "height"
console.log(rooter[arb])

Happy days!

However, if I have a more deeply nested object, and I want to get a leaf described by an address in a string...

var rooter = {
    user: {
        firstName: 'Billy',
        lastName: 'Moon',
        arbitrary: {
            namespace: {
                height: '1.4m'
            }
        }
    }
}


var name = "user.lastName"
console.log(rooter[name]) // does not work

var arb = "user.arbitrary.namespace.height"
console.log(rooter[arb]) // does not work

No dice :(

How can I access arbitrary object leaves from a string describing the path?

EDIT: Found method with underscore...

_.reduce(arb.split('.'), function(m, n){ return m[n] }, rooter)

and for IE 9 and above...

arb.split('.').reduce(function(m, n){ return m[n] }, rooter)
Billy Moon
  • 57,113
  • 24
  • 136
  • 237
  • 5
    Possibly relevant: http://stackoverflow.com/questions/9338192/access-object-through-dot-syntax-string-path?rq=1 – Waleed Khan Sep 24 '13 at 14:29
  • yes - looks like the same question - it is very hard to search for. I think the answer seems overly complex though - there must be a simpler way. – Billy Moon Sep 24 '13 at 14:30
  • Question to the community, is _I don't like that answer_ a valid case for a duplicate question? – Evan Davis Sep 24 '13 at 14:32
  • You can't really get simpler than using `.split` on your `"user.arbitrary.namespace.height"` strings, because object property access in JavaScript was built such that you have to use multiple bracket notations (`obj['prop1']['prop2']...`) if you have strings of your property names. I would encapsulate it in a function, as the answers below have done and the linked answer has done, so that you can write once and forget it. – ajp15243 Sep 24 '13 at 14:34
  • @Mathletics The linked answer doesn't use a "good-looking" code (too short variable names). – ComFreek Sep 24 '13 at 14:36
  • 3
    [There's a huge lot of duplicates](http://stackoverflow.com/a/14397052/1048572). And no, that solution is not complicated. – Bergi Sep 24 '13 at 14:36
  • Sure, but now you're including a whole library for something that can be done simply. Potato/potato I guess. – Evan Davis Sep 24 '13 at 14:41
  • Thanks everyone - I think I am going to go with `reduce` solution, and use shim where necessary (not in my case right now). – Billy Moon Sep 24 '13 at 14:48
  • Actually, I think I might use the answer from the duplicate question, now I understand it a bit better – Billy Moon Sep 24 '13 at 15:04
  • Or maybe the answer from @Scott actually - as it is very elegant, simple and easy to understand. – Billy Moon Sep 24 '13 at 15:11

4 Answers4

1

You can use eval (which is usually a bad idea):

eval('rooter.' + name);

Probably better way for doing this will be:

function namespace(namespaceString) {
    var parts = namespaceString.split('.'),
        parent = window,
        currentPart = '';    

    for(var i = 0, length = parts.length; i < length; i++) {
        currentPart = parts[i];
        parent[currentPart] = parent[currentPart] || {};
        parent = parent[currentPart];
    }

    return parent;
}

console.log(namespace('rooter.user.firstName'));
Minko Gechev
  • 25,304
  • 9
  • 61
  • 68
1

I looked around a bit and found: Access object child properties using a dot notation string which seems to be an answer to your question.

There are a few ways that you could go about accessing different descendants in a object. For example, you could do rooter['name']['arbitrary']['namespace']['height'], in order to get the value of height. But this seems as thought it may not be exactly what you were looking for.

In that post, the answer ended up being that you would write a method of your own in order to do it, in which you would take in a string, delimited by dots and split it. Then you would find that element and return the object.

Community
  • 1
  • 1
Scott
  • 26
  • 2
0

Checkout this code:

function access(obj, prop, defVal) {
    var objectNames = prop.split(".");

    var curObj = obj;

    for (var i = 0, len = objectNames.length; i < len; i++) {
        curObj = curObj[objectNames[i]];
        if (typeof curObj === 'undefined') {
            return defVal;
        }
    }
    return curObj;
}

jsFiddle

ComFreek
  • 29,044
  • 18
  • 104
  • 156
0

Recursive method (from this question)

var rooter = { user: { firstName: 'Billy', lastName: 'Moon', arbitrary: { namespace: { height: '1.4m' } } } };

var arb = "user.arbitrary.namespace.height";

fromPathString = function(pathString, targetObject){
    pathString = pathString.split('.');
    for(var i = 0; i < pathString.length; i++){
        targetObject = (targetObject[pathString[i]])? targetObject[pathString[i]] : targetObject[pathString[i]] = {};
    };
    return targetObject
}

console.log(fromPathString(arb, rooter))

Reduce method

arb.split('.').reduce(function(m, n){ return m[n] }, rooter)

Because .reduce is not compatible with all browsers...

Feature Chrome  Firefox (Gecko) Internet Explorer   Opera   Safari
Basic support   (Yes)   3.0 (1.9)   9   10.5    4.0

Shim for IE 9 and below... (from MDN)

Just put before calling .reduce on any array...

if ('function' !== typeof Array.prototype.reduce) {
  Array.prototype.reduce = function(callback, opt_initialValue){
    'use strict';
    if (null === this || 'undefined' === typeof this) {
      // At the moment all modern browsers, that support strict mode, have
      // native implementation of Array.prototype.reduce. For instance, IE8
      // does not support strict mode, so this check is actually useless.
      throw new TypeError(
          'Array.prototype.reduce called on null or undefined');
    }
    if ('function' !== typeof callback) {
      throw new TypeError(callback + ' is not a function');
    }
    var index, value,
        length = this.length >>> 0,
        isValueSet = false;
    if (1 < arguments.length) {
      value = opt_initialValue;
      isValueSet = true;
    }
    for (index = 0; length > index; ++index) {
      if (this.hasOwnProperty(index)) {
        if (isValueSet) {
          value = callback(value, this[index], index, this);
        }
        else {
          value = this[index];
          isValueSet = true;
        }
      }
    }
    if (!isValueSet) {
      throw new TypeError('Reduce of empty array with no initial value');
    }
    return value;
  };
}
Community
  • 1
  • 1
Billy Moon
  • 57,113
  • 24
  • 136
  • 237