if perf is a serious consideration and you're reusing the same paths a lot (e.g. inside an array filter function), use the Function constructor as described in my answer below. When the same path is used thousands of times, the Function method can be more than 10x as fast as evaling or splitting and reducing the path on every dereference.
–
Kevin CrumleyMay 20 at 22:25

13 Answers
13

recent note: While I'm flattered that this answer has gotten many upvotes, I am also somewhat horrified. If one needs to convert dot-notation strings like "x.a.b.c" into references, it's probably a sign that there is something very wrong going on (unless maybe you're performing some strange deserialization). Expect massive performance hits as well if you do this more than you need to (e.g. as your app's default form of passing objects around and dereferencing them). If for some reason this is server-side js, the usual holds for sanitization of inputs. Novices who find their way to this answer should consider working with array representations instead, e.g. ['x','a','b','c'], or even something more direct/simple/straightforward if possible, like not losing track of the references themselves, or maybe some pre-existing unique id, etc.

Here's an elegant one-liner that's 10x shorter than the other solutions:

(Not that I think eval always bad like others suggest it is (though it usually is), nevertheless those people will be pleased that this method doesn't use eval. The above will find obj.a.b.etc given obj and the string "a.b.etc".)

In response to those who still are afraid of using reduce despite it being in the ECMA-262 standard (5th edition), here is a two-line recursive implementation:

how would you turn this into a setter as well? Not only returning the values by path, but also setting them if a new value is sent into the function? – Swader Jun 28 at 21:42

(sidenote: sadly can't return an object with a Setter, as that would violate the calling convention; commenter seems to instead be referring to a general setter-style function with side-effects like index(obj,"a.b.etc", value) doing obj.a.b.etc = value.)

The reduce style is not really suitable to that, but we can modify the recursive implementation:

...though personally I'd recommend making a separate function setIndex(...). I would like to end on a side-note that the original poser of the question could (should?) be working with arrays of indices (which they can get from .split), rather than strings; though there's usually nothing wrong with a convenience function.

Thanks for all the quick responses. Don't like the eval() solutions. This and the similar posts looks best. However I'm still having a problem. I am trying to set the value obj.a.b = new value. To be precise b's value is a function so I need to use obj.a.b( new_value ). The function is called but the value isn't set. I think it's a scope issue but I'm still digging. I realize this is outside of the scope of the original question. My code is using Knockout.js and b is an ko.observable.
–
nevfJun 18 '11 at 5:16

@nevf: I added a second function that I think does what you want. You can customize it to your liking depending on the behavior you want (e.g. should it create the objects if they do not exist?, etc.).
–
Cristian SanchezJun 18 '11 at 5:25

@nevf But mine does it with one function. ;D
–
tylermwashburnJun 18 '11 at 5:32

thanks for the update which I was able to use. @tylermwashburn - and thanks for your shorter implementation which also works a treat. Have a great w/e all.
–
nevfJun 18 '11 at 6:59

@nevf: I didn't realize this was a golfing contest...
–
Cristian SanchezJun 18 '11 at 8:23

If you expect to dereference the same path many times, building a function for each dot notation path actually has the best performance by far (expanding on the perf tests James Wilkins linked to in comments above).

Using the Function constructor has some of the same drawbacks as eval() in terms of security and worst-case performance, but IMO it's a badly underused tool for cases where you need a combination of extreme dynamism and high performance. I use this methodology to build array filter functions and call them inside an AngularJS digest loop. My profiles consistently show the array.filter() step taking less than 1ms to dereference and filter about 2000 complex objects, using dynamically-defined paths 3-4 levels deep.

A similar methodology could be used to create setter functions, of course:

if you need to dereference the same paths a long time apart, the jsperf.com link above shows an example of how to save and look up the function later. The act of calling the Function constructor is fairly slow, so high-perf code should memoize the results to avoid repeating it if possible.
–
Kevin CrumleyMay 20 at 22:06

This doesn't work for a.b.c, and doesn't really accomplish what they want.. They want the value, not an eval path.
–
tylermwashburnJun 18 '11 at 5:35

I now fixed it so it works with a.b.c, but you are right, apparently he wanted to get/set the value of the property at obj.a.b. The question was confusing to me, since he said he wanted to "convert the string"....
–
Mark EirichJun 18 '11 at 5:48

Good job. :) It was a little vague. You did a good job of conversion though.
–
tylermwashburnJun 18 '11 at 5:53