93

Possible Duplicate:
Accessing nested JavaScript objects with string key

Maybe the title is not clear enough, I just didn't know how to specify what I'm looking for and my English is really bad, sorry.

I'm trying to create function that returns object value, but also plays nice with nested objects. For example:

var obj = {
  foo: { bar: 'baz' }
};

I want to access the value of obj.foo.bar by suppling the string "foo.bar" to the function.

function(obj, path) {
  // Path can be "foo.bar", or just "foo".
}

Thanks!

3
  • 3
    This is now supported by lodash using _.get(obj, property). See lodash.com/docs#get Commented Aug 22, 2017 at 10:21
  • 1
    Since this question was marked as a Duplicate (even it's not) I have to answer in comment. You can use ECMAScript6 Destructuring: var obj = {foo: { bar: 'baz' }};({foo:{bar:value}} = obj);console.log(value); Commented Feb 21, 2018 at 14:45
  • If you want to properly handle any issue while retrieving the value, plus intelligent handling of functions, check out path-value to help with that. Commented Dec 25, 2020 at 10:36

5 Answers 5

111

This works correctly:

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;
};

Here is the proof / demo: jsfiddle.net/tadeck/5Pt2q/13/

EDIT: I have removed redundant variables, shortened the code.

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

14 Comments

beautiful. better than anything in the duplicate stackoverflow.com/questions/6491463/…
@SteveBlack better how? It supports a more restricted syntax, and has no error checking for attempting to resolve a key that doesn't exist.
I think it would be safer to check for obj[path[i]] === undefined
It's bad practice to mutate arguments.
same for ES6: const deep_value = (o, p) => p.split('.').reduce((a, v) => a[v], o);
|
66

Consider this:

var obj = {
  foo: { bar: 'baz' }
};

function deepFind(obj, path) {
  var paths = path.split('.')
    , current = obj
    , i;

  for (i = 0; i < paths.length; ++i) {
    if (current[paths[i]] == undefined) {
      return undefined;
    } else {
      current = current[paths[i]];
    }
  }
  return current;
}

console.log(deepFind(obj, 'foo.bar'))

3 Comments

@7elephant and qiao: Fails (throws error) if any part of the path evaluates null. That could be a feature or a bug, depending on your viewpoint. :-)
how can you handle array inside object?
@mohammadpakivand i beleive you can simply use a number instead of a string: example: path = 'myprop.0' will access the first index of the array under 'myprop'
18

You mean something like this ? It is a recursive version

function recLookup(obj, path) {
    parts = path.split(".");
    if (parts.length==1){
        return obj[parts[0]];
    }
    return recLookup(obj[parts[0]], parts.slice(1).join("."));
}

See http://jsfiddle.net/kExSr/

2 Comments

GOLD! I was able to use this to map a path /Root[1]/This/worked to an object {"Root[1]":This{worked:"value"}} . had to trim the leading / and change . to / but otherwise beautiful.
this doesn't look too fast. Shouldn't join() the paths again, just check if path is a string or an array
11

something like:

function(obj, path) {
  var current=obj; 
  path.split('.').forEach(function(p){ current = current[p]; }); 
  return current;
}

3 Comments

Another way without mutation: const lens = (obj, path) => path.split(".").reduce((o, key) => o && o[key] ? o[key] : null, obj);
@YuriyGorbylov almost perfect, added a typeof to allow falsy values (0 etc.): (obj, path) => path.split(".").reduce((o, key) => o && typeof o[key] !== 'undefined' ? o[key] : undefined, obj)
8

You'd want to split the string on the dot and then repeatedly index into the object, e.g. along the lines of:

function goDeep(obj, path) {
    var parts = path.split('.'),
        rv,
        index;
    for (rv = obj, index = 0; rv && index < parts.length; ++index) {
        rv = rv[parts[index]];
    }
    return rv;
}

Live example

That works because you can access the property of an object in a couple of different ways: There's dotted syntax using a literal (obj.foo), and there's bracketed syntax using a string (obj["foo"]). In the latter case, the string can be the result of any expression, it doesn't have to be a string literal. In in all of the, rv is set to the same value:

rv = obj.foo.bar;
// Or
rv = obj.foo["bar"];
// Or
f = "foo";
rv = obj[f].bar;
// Or
s = "b";
rv = obj.foo[s + "ar"];

4 Comments

Surely, the requirement was not expressed, but might we not suspect that OP might equally well want a function call such as goDeep(myObj, 'bar[3].baz')? That may be out of scope for the current question...
@DavidHedlund: Fair point, it may well be useful to check for the bracketed form within each part in order to be fully compatible with JavaScript's own syntax. I'll leave it as an exercise for the OP. :-)
(Well, not fully, as to do that you'd have to basically re-invent [or -shudder- use] eval. But say, mostly compatible with.)
Well granted, mostly compatible is probably preferable over fully compatible in this case :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.