tags:

views:

54

answers:

5

If I have a JavaScript object such as:

var x = {foo: 42, bar: {fubar: true}}

then I can get the value true with var flag = x.bar.fubar. I'd like to be able to separate out and store the path "bar.fubar", then evaluate it dynamically. Something like:

var paths = ["bar.fubar", ...];
...
var flag = evalPath( x, paths[0] );

Obviously I could write a simple parser and evaluator for a basic path expression grammar. But under DRY principles I wonder if there's already an existing way to do something like evalPath built-in to JavaScript, or a small library that would do the job? I also anticipate needing array indexes in the path expression in future.

Update Just to be clear, I'm not asking for code samples - my question is whether there's existing code (built-in or library) I can re-use. Thanks to the contributors below for suggestions of code samples anyway! Note that none of them handle the array index requirement.

A: 

JavaScript provides eval, but I don't recommend it.

Hank Gay
Indeed. I'm aware of `eval`, and it's not what I'm looking for.
Ian Dickinson
A: 

You could try something like this. I can't really think of a situation where it would be appropriate to store paths this way though.

function evalPath(obj, path) {
  var pathLevels = path.split('.');
  var result = obj;
  for (var i = 0; i < pathLevels.length; i++) {
    try {
      result = result[pathLevels[i]];
    }
    catch (e) {
      alert('Property not found:' + pathLevels[i]);
    }
  }
  return result;
}

The alert is really only there for debugging purposes. You may want to return null or something.

ntownsend
Thanks ntownsend. I wasn't actually asking for code, I was asking whether there's a built-in way or a library to do this so I don't have to repeat (and write tests for!) code that's already out there.
Ian Dickinson
By the way the use case is that I have a AJAX server serving up a fairly complex JSON data structure. The user can project out a variety of facets of the data structure into the UI; the path expressions will be stored as part of an extensible collection of declarative facet specifications. Representing how to map the data from the model to the view, essentially.
Ian Dickinson
Ah. Then storing the paths would be appropriate. I've never seen any functionality like this in a library. You may be stuck with doing it yourself :)
ntownsend
+1  A: 

Why not try something like

function evalPath(obj, path)
{
    var rtnValue = obj;

    // Split our path into an array we can iterate over
    var path = path.split(".");
    for (var i = 0, max=path.length; i < max; i++) 
    {
        // If setting current path to return value fails, set to null and break
        if (typeof (rtnValue = rtnValue[path[i]]) == "undefined")
        {
            rtnValue = null;
            break;
        }
    }

    // Return the final path value, or null if it failed
    return rtnValue;
}

Not tested, but it should work fairly well. Like XPath, it will return null if it can't find what it's looking for.

Andy E
Thanks Andy. I wasn't actually asking for code, I was asking whether there's a built-in way or a library to do this so I don't have to repeat (and write tests for!) code that's already out there.
Ian Dickinson
@Ian Dickinson: fair enough. I haven't ever come across such a thing. I have searched for the same thing on several occasions, just because sometimes it would be easier to have a statement evaluate to null instead of throwing an error because a certain property didn't exist. Kind of like using XPath instead of `documentElement.childNodes[0].childNodes[1].childNodes[47]`
Andy E
Hmm, the XPath analogy was a good suggestion, thanks. I searched "JSON xpath" and came up with: http://goessner.net/articles/JsonPath/. Looks interesting, but I've not had chance to look at it in detail yet.
Ian Dickinson
@Ian Dickinson: I never found that when I was searching :-) Good luck.
Andy E
A: 

like

function locate(obj, path) {
    var p = path.split("."), a = p.shift();
    if(a in obj)
       return p.length ? locate(obj[a], p.join(".")) : obj[a];
     return undefined;
}

locate(x, "bar.fubar")

this works on the right only, of course

stereofrog
Thanks stereofrog. I wasn't actually asking for code, I was asking whether there's a built-in way or a library to do this so I don't have to repeat (and write tests for!) code that's already out there.
Ian Dickinson
eval is a built-in way to do this.
stereofrog
+1  A: 

Doing a quick search, I came across JSONPath. Haven't used it at all, but it looks like it might do what you want it to.

Example usage:

var x = {foo: 42, bar: {fubar: true}}
var res1 = jsonPath(x, "$.bar.fubar"); // Array containing fubar's value
cmptrgeekken
Thanks cmptrgeekken, I also found the same package following a suggestion from Andy E. jsonPath seems to be working well so far
Ian Dickinson