By Komaruloh


2011-06-27 10:25:02 8 Comments

I have a data structure like this :

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }, {
            'name': 'Part 3B',
            'size': '5',
            'qty' : '20'
        }, {
            'name': 'Part 3C',
            'size': '7.5',
            'qty' : '20'
        }
    ]
};

And I would like to access the data using these variable :

var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";

part1name should be filled with someObject.part1.name 's value, which is "Part 1". Same thing with part2quantity which filled with 60.

Is there anyway to achieve this with either pure javascript or JQuery?

29 comments

@Mohamad Hamouday 2018-11-28 23:35:57

Inspired by @webjay's answer: https://stackoverflow.com/a/46008856/4110122

I made this function which can you use it to Get/ Set/ Unset any value in object

function Object_Manager(obj, Path, value, Action) 
{
    try
    {
        if(Array.isArray(Path) == false)
        {
            Path = [Path];
        }

        let level = 0;
        var Return_Value;
        Path.reduce((a, b)=>{
            level++;
            if (level === Path.length)
            {
                if(Action === 'Set')
                {
                    a[b] = value;
                    return value;
                }
                else if(Action === 'Get')
                {
                    Return_Value = a[b];
                }
                else if(Action === 'Unset')
                {
                    delete a[b];
                }
            } 
            else 
            {
                return a[b];
            }
        }, obj);
        return Return_Value;
    }

    catch(err)
    {
        console.error(err);
        return obj;
    }
}

To use it:

 // Set
 Object_Manager(Obj,[Level1,Level2,Level3],New_Value, 'Set');

 // Get
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Get');

 // Unset
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Unset');

@Nick Grealy 2018-06-26 18:16:45

This will probably never see the light of day... but here it is anyway.

  1. Replace [] bracket syntax with .
  2. Split on . character
  3. Remove blank strings
  4. Find the path (otherwise undefined)

// "one liner" (ES6)

const deep_value = (obj, path) => 
  path
    .replace(/\[|\]\.?/g, '.')
    .split('.')
    .filter(s => s)
    .reduce((acc, val) => acc && acc[val], obj);
    
// ... and that's it.

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }
        // ...
    ]
};

console.log(deep_value(someObject, "part1.name"));               // Part 1
console.log(deep_value(someObject, "part2.qty"));                // 60
console.log(deep_value(someObject, "part3[0].name"));            // Part 3A

@abann sunny 2019-01-15 21:01:13

It did see the light of the day!! thanks

@speigg 2014-03-02 16:06:11

This is the solution I use:

function resolve(path, obj=self, separator='.') {
    var properties = Array.isArray(path) ? path : path.split(separator)
    return properties.reduce((prev, curr) => prev && prev[curr], obj)
}

Example usage:

// accessing property path on global scope
resolve("document.body.style.width")
// or
resolve("style.width", document.body)

// accessing array indexes
// (someObject has been defined in the question)
resolve("part3.0.size", someObject) // returns '10'

// accessing non-existent properties
// returns undefined when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})

// accessing properties with unusual keys by changing the separator
var obj = { object: { 'a.property.name.with.periods': 42 } }
resolve('object->a.property.name.with.periods', obj, '->') // returns 42

// accessing properties with unusual keys by passing a property name array
resolve(['object', 'a.property.name.with.periods'], obj) // returns 42

Limitations:

  • Can't use brackets ([]) for array indices—though specifying array indices between the separator token (e.g., .) works fine as shown above.

@Alp 2014-05-22 14:51:56

using reduce is an excellent solution (one can also use _.reduce() from the underscore or lodash library)

@Platinum Azure 2014-06-17 03:37:44

I think self is probably undefined here. Do you mean this?

@speigg 2014-06-18 16:03:19

No, self is defined. this would be incorrect.

@Rahil Wazir 2014-11-07 10:15:30

@Slavo Vojacek 2015-06-10 22:39:02

lodash + typescript implementation: _.reduce(path.split('.'), (previous, current) => !safe ? previous[current] : (previous ? previous[current] : undefined), target || self);

@mroach 2017-01-02 08:42:40

Here's my complement to set values by path: pastebin.com/jDp5sKT9

@Adam Plocher 2017-07-31 14:05:19

Anyone know how to port this to TypeScript?

@SC1000 2018-07-27 08:31:52

It would be better to use a default object in the signature, i.e. function resolve(path, obj={}) and remove the "|| self" part, which refers to the window (global) object.

@speigg 2018-08-15 00:29:23

@SC1000 good idea. This answer was written before default parameters were available in most browsers. I'll update it to "function resolve(path, obj=self)", since referencing the global object as a default is intentional.

@speigg 2018-08-15 00:41:21

I parametrized the "separator" value as well, in order to support non-typical property names.

@speigg 2018-08-15 01:06:20

Added support for using a property array as input, and updated to full ES6 syntax.

@Jaider 2019-02-04 07:31:57

you can also do: function resolve(properties, obj=self) { return properties.reduce((prev, curr) => prev && prev[curr], obj) } and you can use like this resolve(["style","width"], document.body)

@Dinesh Pandiyan 2018-07-08 13:01:53

Just in case, anyone's visiting this question in 2017 or later and looking for an easy-to-remember way, here's an elaborate blog post on Accessing Nested Objects in JavaScript without being bamboozled by

Cannot read property 'foo' of undefined error

Access Nested Objects Using Array Reduce

Let's take this example structure

const user = {
    id: 101,
    email: '[email protected]',
    personalInfo: {
        name: 'Jack',
        address: [{
            line1: 'westwish st',
            line2: 'washmasher',
            city: 'wallas',
            state: 'WX'
        }]
    }
}

To be able to access nested arrays, you can write your own array reduce util.

const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

// pass in your object structure as array elements
const name = getNestedObject(user, ['personalInfo', 'name']);

// to access nested array, just pass in array index as an element the path array.
const city = getNestedObject(user, ['personalInfo', 'address', 0, 'city']);
// this will return the city from the first address item.

There is also an excellent type handling minimal library typy that does all this for you.

With typy, your code will look like this

const city = t(user, 'personalInfo.address[0].city').safeObject;

Disclaimer: I am the author of this package.

@Akash 2018-06-22 11:49:45

Working with Underscore's property or propertyOf:

var test = {
  foo: {
    bar: {
      baz: 'hello'
    }
  }
}
var string = 'foo.bar.baz';


// document.write(_.propertyOf(test)(string.split('.')))

document.write(_.property(string.split('.'))(test));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>

Good Luck...

@Oboo Chin 2018-02-23 12:37:22

// (IE9+) Two steps

var pathString = "[0]['property'].others[3].next['final']";
var obj = [{
  property: {
    others: [1, 2, 3, {
      next: {
        final: "SUCCESS"
      }
    }]
  }
}];

// Turn string to path array
var pathArray = pathString
    .replace(/\[["']?([\w]+)["']?\]/g,".$1")
    .split(".")
    .splice(1);

// Add object prototype method
Object.prototype.path = function (path) {
  try {
    return [this].concat(path).reduce(function (f, l) {
      return f[l];
    });
  } catch (e) {
    console.error(e);
  }
};

// usage
console.log(obj.path(pathArray));
console.log(obj.path([0,"doesNotExist"]));

@Flavien Volken 2017-12-15 13:54:22

While reduce is good, I am surprised no one used forEach:

function valueForKeyPath(obj, path){
        const keys = path.split('.');
        keys.forEach((key)=> obj = obj[key]);
        return obj;
    };

Test

@Carles Alcolea 2018-07-22 04:08:37

You are not even checking if obj[key] actually exists. It's unreliable.

@Flavien Volken 2018-07-23 07:57:21

@CarlesAlcolea by default js will neither check if the key of an object exists: a.b.c will raise an exception if there is no property b in your object. If you need something silently dismissing the wrong keypath (which I do not recommend), you can still replace the forEach with this one keys.forEach((key)=> obj = (obj||{})[key]);

@Carles Alcolea 2018-07-28 23:59:32

I run through it an object that was missing a curly brace, my bad :)

@James 2016-12-08 16:19:48

It's a one liner with lodash.

const deep = { l1: { l2: { l3: "Hello" } } };
const prop = "l1.l2.l3";
const val = _.reduce(prop.split('.'), function(result, value) { return result ? result[value] : undefined; }, deep);
// val === "Hello"

Or even better...

const val = _.get(deep, prop);

Or ES6 version w/ reduce...

const val = prop.split('.').reduce((r, val) => { return r ? r[val] : undefined; }, deep);

Plunkr

@Vincent 2017-11-01 12:44:48

Based on a previous answer, I have created a function that can also handle brackets. But no dots inside them due to the split.

function get(obj, str) {
  return str.split(/\.|\[/g).map(function(crumb) {
    return crumb.replace(/\]$/, '').trim().replace(/^(["'])((?:(?!\1)[^\\]|\\.)*?)\1$/, (match, quote, str) => str.replace(/\\(\\)?/g, "$1"));
  }).reduce(function(obj, prop) {
    return obj ? obj[prop] : undefined;
  }, obj);
}

@Jodo 2017-10-25 08:12:18

Instead of a string an array can be used adressing nested objects and arrays e.g.: ["my_field", "another_field", 0, "last_field", 10]

Here is an example that would change a field based on this array representation. I am using something like that in react.js for controlled input fields that change the state of nested structures.

let state = {
        test: "test_value",
        nested: {
            level1: "level1 value"
        },
        arr: [1, 2, 3],
        nested_arr: {
            arr: ["buh", "bah", "foo"]
        }
    }

function handleChange(value, fields) {
    let update_field = state;
    for(var i = 0; i < fields.length - 1; i++){
        update_field = update_field[fields[i]];
    }
    update_field[fields[fields.length-1]] = value;
}

handleChange("update", ["test"]);
handleChange("update_nested", ["nested","level1"]);
handleChange(100, ["arr",0]);
handleChange('changed_foo', ["nested_arr", "arr", 3]);
console.log(state);

@Adriano Spadoni 2017-05-08 13:38:34

ES6: Only one line in Vanila JS (it return null if don't find instead of giving error):

'path.string'.split('.').reduce((p,c)=>p&&p[c]||null, MyOBJ)

or exemple:

'a.b.c'.split('.').reduce((p,c)=>p&&p[c]||null, {a:{b:{c:1}}})

For a ready to use function that also recognizes false, 0 and negative number and accept default values as parameter:

const resolvePath = (object, path, defaultValue) => path
   .split('.')
   .reduce((o, p) => o ? o[p] : defaultValue, object)

Exemple to use:

resolvePath(window,'document.body') => <body>
resolvePath(window,'document.body.xyz') => undefined
resolvePath(window,'document.body.xyz', null) => null
resolvePath(window,'document.body.xyz', 1) => 1

Bonus:

To set a path (Requested by @rob-gordon) you can use:

const setPath = (object, path, value) => path
   .split('.')
   .reduce((o,p) => o[p] = path.split('.').pop() === p ? value : o[p] || {}, object)

Example:

let myVar = {}
setPath(myVar, 'a.b.c', 42) => 42
console.log(myVar) => {a: {b: {c: 42}}}

Access array with []:

const resolvePath = (object, path, defaultValue) => path
   .split(/[\.\[\]\'\"]/)
   .filter(p => p)
   .reduce((o, p) => o ? o[p] : defaultValue, object)

exemple

const myVar = {a:{b:[{c:1}]}}
resolvePath(myVar,'a.b[0].c') => 1
resolvePath(myVar,'a["b"][\'0\'].c') => 1

@rob-gordon 2017-06-28 17:15:05

I love this technique. This is really messy but I wanted to use this technique for assignment. let o = {a:{b:{c:1}}}; let str = 'a.b.c'; str.split('.').splice(0, str.split('.').length - 1).reduce((p,c)=>p&&p[c]||null, o)[str.split('.').slice(-1)] = "some new value";

@SmujMaiku 2017-10-15 17:31:16

I like the idea of using reduce but your logic seems off for 0, undefined and null values. {a:{b:{c:0}}} returns null instead of 0. Perhaps explicitly checking for null or undefined will clear up these issues. (p,c)=>p === undefined || p === null ? undefined : p[c]

@Adriano Spadoni 2017-10-16 09:18:27

Hi @SmujMaiku, the "ready to use" function return correctly for '0', 'undefined' and 'null', I just tested on the console: resolvePath({a:{b:{c:0}}},'a.b.c',null) => 0; It check if the key exists instead of the value itself which avoid more than one check

@Andre Figueiredo 2018-02-23 12:31:11

here defaultValue did not work, using Reflect.has(o, k) ? ... (ES6 Reflect.has) worked though

@Tamb 2017-08-02 19:16:40

Building off of Alnitak's answer:

if(!Object.prototype.byString){
  //NEW byString which can update values
Object.prototype.byString = function(s, v, o) {
  var _o = o || this;
      s = s.replace(/\[(\w+)\]/g, '.$1'); // CONVERT INDEXES TO PROPERTIES
      s = s.replace(/^\./, ''); // STRIP A LEADING DOT
      var a = s.split('.'); //ARRAY OF STRINGS SPLIT BY '.'
      for (var i = 0; i < a.length; ++i) {//LOOP OVER ARRAY OF STRINGS
          var k = a[i];
          if (k in _o) {//LOOP THROUGH OBJECT KEYS
              if(_o.hasOwnProperty(k)){//USE ONLY KEYS WE CREATED
                if(v !== undefined){//IF WE HAVE A NEW VALUE PARAM
                  if(i === a.length -1){//IF IT'S THE LAST IN THE ARRAY
                    _o[k] = v;
                  }
                }
                _o = _o[k];//NO NEW VALUE SO JUST RETURN THE CURRENT VALUE
              }
          } else {
              return;
          }
      }
      return _o;
  };

}

This allows you to set a value as well!

I've created an npm package and github with this as well

@Ben 2016-12-26 04:51:19

Simple function, allowing for either a string or array path.

function get(obj, path) {
  if(typeof path === 'string') path = path.split('.');

  if(path.length === 0) return obj;
  return get(obj[path[0]], path.slice(1));
}

const obj = {a: {b: {c: 'foo'}}};

console.log(get(obj, 'a.b.c')); //foo

OR

console.log(get(obj, ['a', 'b', 'c'])); //foo

@Tieson T. 2016-12-26 05:44:53

If you're going to post code as an answer, please explain why the code answers the question.

@Alnitak 2011-06-27 10:40:10

I just made this based on some similar code I already had, it appears to work:

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('.');
    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            o = o[k];
        } else {
            return;
        }
    }
    return o;
}

Usage::

Object.byString(someObj, 'part3[0].name');

See a working demo at http://jsfiddle.net/alnitak/hEsys/

EDIT some have noticed that this code will throw an error if passed a string where the left-most indexes don't correspond to a correctly nested entry within the object. This is a valid concern, but IMHO best addressed with a try / catch block when calling, rather than having this function silently return undefined for an invalid index.

@Komaruloh 2011-06-27 11:28:20

Your answer is exactly what I need. It work like a charm. However Felix Kling came up with different way and work as good as your answer. I just feel like I have to mention his answer as the correct one either. Anyway, thanks alot for the solution you gave me.

@Kimchi Man 2014-08-05 14:23:18

What if the value is null? How can I handle it?

@t3dodson 2015-01-13 22:38:26

This works beautifully. Please contribute this to the internet by wrapping it as a node package.

@Alnitak 2015-03-27 22:23:32

@pero thanks for the fix - I should have tested my change first :(

@Alnitak 2015-04-30 18:56:17

@hsz I rolled back your edit because it's not only the object types that can have properties.

@Royi Namir 2015-07-05 11:12:23

@Alnitak Hi. nice solution. however the code in the jsfiddle doesn't work as expected whereas the code in your answer - does. i.imgur.com/hTMwxOC.png

@Alnitak 2015-07-05 22:13:33

@RoyiNamir thanks - I've fixed the fiddle.

@Capaj 2015-07-30 21:57:27

@t3dodson I just did: github.com/capaj/object-resolve-path just be aware that this doesn't play nice when your property name contains '[]' in itself. Regex will replace it with '.' and it doesn't work as expected

@ian 2015-08-13 12:49:10

great stuff; using the lodash library, one can also do: _.get(object, nestedPropertyString);

@helpse 2015-08-25 14:10:45

I had this issue: Having a property with a dot inside. So I updated this answer: jsfiddle.net/hEsys/359

@ste2425 2015-11-17 11:12:09

This will probably get lost in the sea of comments, however it errors if you try and address a property that doesn't exist. So 'part3[0].name.iDontExist'. Adding a check to see if o is an object in the if in fixes the issue. (How you go about that is up-to you). See updated fiddle: jsfiddle.net/hEsys/418

@Okyo 2015-12-07 12:31:15

Really great! I have one question... dont i encounter the "object-iteration-problem" (See: stackoverflow.com/questions/500504/…), when i use this approach to iterate an Array with "var x in array"?

@Alnitak 2015-12-07 13:00:05

@Okyo only if you've unsafely added a method to Array.prototype as an enumerable property. Sometimes using for .. in can actually be better, because it enumerates over both the numeric indices and any additional string properties that were deliberately added to the array. In any event, this code doesn't actually use for .. in.

@Okyo 2015-12-07 14:50:05

Ah, that's interesting! I thougt using if (k in o) {} leads to a similar problem. But now i understand! Thanx alot...

@yngrdyn 2016-03-22 20:47:55

Useful! I was wondering how to go from a "leveled String" to an element inside a hash table

@Christian Esperar 2016-06-22 15:52:13

This is so gold. We have a config based application and this is kinda helpful! Thanks!

@Jaakko Karhu 2016-06-23 12:21:02

I played with your code and turned it around to be used for storing a value in to a nested object, which is referred by string. Can be found here: jsfiddle.net/jaakkokarhu/omm2v8ty

@Ivan Sander de Jong 2016-07-05 10:23:38

Awsome thanks, I added by the if (k in o) { statement a null check for o, if for some reason the proprty is null the code crashes by adding a null check this always works ( if (o != null && k in o ) {)

@Tarion 2016-11-04 11:03:46

I like to add: if (!s) { return o; } at the beginning.

@Raz 2018-10-21 06:28:52

How to set value?

@Vitim.us 2016-03-01 17:05:57

/**
 * Access a deep value inside a object 
 * Works by passing a path like "foo.bar", also works with nested arrays like "foo[0][1].baz"
 * @author Victor B. https://gist.github.com/victornpb/4c7882c1b9d36292308e
 * Unit tests: http://jsfiddle.net/Victornpb/0u1qygrh/
 */
function getDeepVal(obj, path) {
    if (typeof obj === "undefined" || obj === null) return;
    path = path.split(/[\.\[\]\"\']{1,2}/);
    for (var i = 0, l = path.length; i < l; i++) {
        if (path[i] === "") continue;
        obj = obj[path[i]];
        if (typeof obj === "undefined" || obj === null) return;
    }
    return obj;
}

Works with

getDeepVal(obj,'foo.bar')
getDeepVal(obj,'foo.1.bar')
getDeepVal(obj,'foo[0].baz')
getDeepVal(obj,'foo[1][2]')
getDeepVal(obj,"foo['bar'].baz")
getDeepVal(obj,"foo['bar']['baz']")
getDeepVal(obj,"foo.bar.0.baz[1]['2']['w'].aaa[\"f\"].bb")

@ayushgp 2016-06-23 09:45:46

The solutions here are just for accessing the deeply nested keys. I needed one for accessing, adding, modifying and deleting the keys. This is what I came up with:

var deepAccessObject = function(object, path_to_key, type_of_function, value){
    switch(type_of_function){
        //Add key/modify key
        case 0: 
            if(path_to_key.length === 1){
                if(value)
                    object[path_to_key[0]] = value;
                return object[path_to_key[0]];
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    object[path_to_key[0]] = {};
            }
            break;
        //delete key
        case 1:
            if(path_to_key.length === 1){
                delete object[path_to_key[0]];
                return true;
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    return false;
            }
            break;
        default:
            console.log("Wrong type of function");
    }
};
  • path_to_key: path in an array. You can replace it by your string_path.split(".").
  • type_of_function: 0 for accessing(dont pass any value to value), 0 for add and modify. 1 for delete.

@TheZver 2013-04-24 11:24:37

Works for arrays / arrays inside the object also. Defensive against invalid values.

/**
 * Retrieve nested item from object/array
 * @param {Object|Array} obj
 * @param {String} path dot separated
 * @param {*} def default value ( if result undefined )
 * @returns {*}
 */
function path(obj, path, def){
    var i, len;

    for(i = 0,path = path.split('.'), len = path.length; i < len; i++){
        if(!obj || typeof obj !== 'object') return def;
        obj = obj[path[i]];
    }

    if(obj === undefined) return def;
    return obj;
}

//////////////////////////
//         TEST         //
//////////////////////////

var arr = [true, {'sp ace': true}, true]

var obj = {
  'sp ace': true,
  arr: arr,
  nested: {'dotted.str.ing': true},
  arr3: arr
}

shouldThrow(`path(obj, "arr.0")`);
shouldBeDefined(`path(obj, "arr[0]")`);
shouldBeEqualToNumber(`path(obj, "arr.length")`, 3);
shouldBeTrue(`path(obj, "sp ace")`);
shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback");
shouldBeTrue(`path(obj, "nested['dotted.str.ing'])`);
<script src="https://cdn.rawgit.com/coderek/e7b30bac7634a50ad8fd/raw/174b6634c8f57aa8aac0716c5b7b2a7098e03584/js-test.js"></script>

@Dominic 2013-07-31 23:00:17

Thanks this is the best and most performant answer - jsfiddle.net/Jw8XB/1

@TheZver 2016-03-27 11:32:18

@Endless, I'd like to emphasize the path should separate the items with dots. Braces won't work. I.e. to access first item in array use "0.sp ace".

@Harish Anchu 2015-04-11 10:22:48

You can manage to obtain value of a deep object member with dot notation without any external JavaScript library with the simple following trick:

new Function('_', 'return _.' + path)(obj);

In your case to obtain value of part1.name from someObject just do:

new Function('_', 'return _.part1.name')(someObject);

Here is a simple fiddle demo: https://jsfiddle.net/harishanchu/oq5esowf/

@ArcangelZith 2016-07-29 04:59:32

function deep_value ( obj, path ) { return new Function( 'o', 'return o.' + path )( obj ); }

@James Wilkins 2014-08-21 22:12:32

Here I offer more ways, which seem faster in many respects:

Option 1: Split string on . or [ or ] or ' or ", reverse it, skip empty items.

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var parts = path.split(/\[|\]|\.|'|"/g).reverse(), name; // (why reverse? because it's usually faster to pop off the end of an array)
    while (parts.length) { name=parts.pop(); if (name) origin=origin[name]; }
    return origin;
}

Option 2 (fastest of all, except eval): Low level character scan (no regex/split/etc, just a quick char scan). Note: This one does not support quotes for indexes.

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c = '', pc, i = 0, n = path.length, name = '';
    if (n) while (i<=n) ((c = path[i++]) == '.' || c == '[' || c == ']' || c == void 0) ? (name?(origin = origin[name], name = ''):(pc=='.'||pc=='['||pc==']'&&c==']'?i=n+2:void 0),pc=c) : name += c;
    if (i==n+2) throw "Invalid path: "+path;
    return origin;
} // (around 1,000,000+/- ops/sec)

Option 3: (new: option 2 expanded to support quotes - a bit slower, but still fast)

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c, pc, i = 0, n = path.length, name = '', q;
    while (i<=n)
        ((c = path[i++]) == '.' || c == '[' || c == ']' || c == "'" || c == '"' || c == void 0) ? (c==q&&path[i]==']'?q='':q?name+=c:name?(origin?origin=origin[name]:i=n+2,name='') : (pc=='['&&(c=='"'||c=="'")?q=c:pc=='.'||pc=='['||pc==']'&&c==']'||pc=='"'||pc=="'"?i=n+2:void 0), pc=c) : name += c;
    if (i==n+2 || name) throw "Invalid path: "+path;
    return origin;
}

JSPerf: http://jsperf.com/ways-to-dereference-a-delimited-property-string/3

"eval(...)" is still king though (performance wise that is). If you have property paths directly under your control, there shouldn't be any issues with using 'eval' (especially if speed is desired). If pulling property paths "over the wire" (on the line!? lol :P), then yes, use something else to be safe. Only an idiot would say to never use "eval" at all, as there ARE good reasons when to use it. Also, "It is used in Doug Crockford's JSON parser." If the input is safe, then no problems at all. Use the right tool for the right job, that's it.

@Felix Kling 2011-06-27 10:38:59

You'd have to parse the string yourself:

function getProperty(obj, prop) {
    var parts = prop.split('.');

    if (Array.isArray(parts)) {
        var last = parts.pop(),
        l = parts.length,
        i = 1,
        current = parts[0];

        while((obj = obj[current]) && i < l) {
            current = parts[i];
            i++;
        }

        if(obj) {
            return obj[last];
        }
    } else {
        throw 'parts is not valid array';
    }
}

This required that you also define array indexes with dot notation:

var part3name1 = "part3.0.name";

It makes the parsing easier.

DEMO

@Komaruloh 2011-06-27 11:25:01

@Felix Kling : Your solution does provide me with what I need. And I thank you alot for that. But Alnitak also provide different ways and seem to work either. Since I can only choose one answer, I will choose Alnitak answer. Not that his solution is better than you or something like that. Anyway, I really appreciate your solution and effort you gave.

@Felix Kling 2011-06-27 11:50:16

@Komaruloh: Oh I thought you can always up vote answers on your own question.... anyway I was more or less kidding, I don't need more reputation ;) Happy coding!

@Komaruloh 2011-06-27 14:58:13

@Felix Kling : You need at least 15 reputation to up vote. :) I believe you don't need more reputation with 69k+ . Thanks

@Alnitak 2011-06-27 16:19:04

@Felix FWIW - converting from [] syntax to property syntax is pretty trivial.

@hikaru 2014-05-01 16:02:09

I like this answer because I can give my users a simpler format for the paths - using dot notation for indexes instead of brackets. Thanks!

@Snea 2014-08-17 06:18:15

If you change the while loop to while (l > 0 && (obj = obj[current]) && i < l) then this code works for strings without dots as well.

@CuddleBunny 2015-06-01 16:12:19

Honestly, this is the better answer because you can actually change the value of obj[last] but you can't change the value if you did it another way.

@Ian Walker-Sperber 2015-07-08 20:59:00

This is now supported by lodash using _.get(obj, property). See https://lodash.com/docs#get

Example from the docs:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// → 3

_.get(object, ['a', '0', 'b', 'c']);
// → 3

_.get(object, 'a.b.c', 'default');
// → 'default'

@Capaj 2015-08-04 02:12:37

This should be the only accepted answer, because this is the only one working for both dot and bracket syntax and It doesn't fail, when we have '[]' in the string of a key in the path.

@Josh C. 2017-08-22 06:23:54

This. Plus, it supports _.set(...)

@DDave 2017-10-31 16:58:42

what happes if the objet is not found?

@Ian Walker-Sperber 2017-11-02 00:40:33

@DDave if the value passed as the object is undefined or not an object, _.get will show the same behavior as when no key is found in the provided object. eg _.get(null, "foo") -> undefined, _.get(null, "foo", "bar") -> "bar". However this behavior is not defined in the docs so subject to change.

@Andre Figueiredo 2018-02-19 21:21:06

@Capaj you kiddin'? And who doesn't want/can't use lodash?

@Kyle 2015-04-25 01:08:31

I haven't yet found a package to do all of the operations with a string path, so I ended up writing my own quick little package which supports insert(), get() (with default return), set() and remove() operations.

You can use dot notation, brackets, number indices, string number properties, and keys with non-word characters. Simple usage below:

> var jsocrud = require('jsocrud');

...

// Get (Read) ---
> var obj = {
>     foo: [
>         {
>             'key w/ non-word chars': 'bar'
>         }
>     ]
> };
undefined

> jsocrud.get(obj, '.foo[0]["key w/ non-word chars"]');
'bar'

https://www.npmjs.com/package/jsocrud

https://github.com/vertical-knowledge/jsocrud

@nesinervink 2015-02-26 12:22:42

Speigg's approach is very neat and clean, though I found this reply while searching for the solution of accessing AngularJS $scope properties by string path and with a little modification it does the job:

$scope.resolve = function( path, obj ) {
    return path.split('.').reduce( function( prev, curr ) {
        return prev[curr];
    }, obj || this );
}

Just place this function in your root controller and use it any child scope like this:

$scope.resolve( 'path.to.any.object.in.scope')

@caleb 2014-06-13 18:56:11

There is an npm module now for doing this: https://github.com/erictrinh/safe-access

Example usage:

var access = require('safe-access');
access(very, 'nested.property.and.array[0]');

@Shanimal 2012-12-20 14:13:31

using eval:

var part1name = eval("someObject.part1.name");

wrap to return undefined on error

function path(obj, path) {
    try {
        return eval("obj." + path);
    } catch(e) {
        return undefined;
    }
}

http://jsfiddle.net/shanimal/b3xTw/

Please use common sense and caution when wielding the power of eval. It's a bit like a light saber, if you turn it on there's a 90% chance you'll sever a limb. Its not for everybody.

@James Wilkins 2014-08-21 22:39:48

Whether or not eval is a good idea depends on where the property string data is coming from. I doubt you have any reason to be concerned for hackers breaking in via a static "var p='a.b.c';eval(p);" type call. It's a perfectly fine idea for that.

@Jonan Georgiev 2013-12-04 19:54:04

What about this solution:

setJsonValue: function (json, field, val) {
  if (field !== undefined){
    try {
      eval("json." + field + " = val");
    }
    catch(e){
      ;
    }
  }  
}

And this one, for getting:

getJsonValue: function (json, field){
  var value = undefined;
  if (field !== undefined) {
    try {
      eval("value = json." + field);
    } 
    catch(e){
      ;
    }
  }
  return value;
};

Probably some will consider them unsafe, but they must be much faster then, parsing the string.

@abernier 2013-10-25 19:33:20

Just had the same question recently and successfully used https://npmjs.org/package/tea-properties which also set nested object/arrays :

get:

var o = {
  prop: {
    arr: [
      {foo: 'bar'}
    ]
  }
};

var properties = require('tea-properties');
var value = properties.get(o, 'prop.arr[0].foo');

assert(value, 'bar'); // true

set:

var o = {};

var properties = require('tea-properties');
properties.set(o, 'prop.arr[0].foo', 'bar');

assert(o.prop.arr[0].foo, 'bar'); // true

@Patrick Fisher 2014-03-18 00:09:17

"This module has been discontinued. Use chaijs/pathval."

@Hogan 2011-06-27 10:28:27

I think you are asking for this:

var part1name = someObject.part1.name;
var part2quantity = someObject.part2.qty;
var part3name1 =  someObject.part3[0].name;

You could be asking for this:

var part1name = someObject["part1"]["name"];
var part2quantity = someObject["part2"]["qty"];
var part3name1 =  someObject["part3"][0]["name"];

Both of which will work


Or maybe you are asking for this

var partName = "part1";
var nameStr = "name";

var part1name = someObject[partName][nameStr];

Finally you could be asking for this

var partName = "part1.name";

var partBits = partName.split(".");

var part1name = someObject[partBits[0]][partBits[1]];

@duri 2011-06-27 10:37:00

I think OP's asking for the last solution. However, strings don't have Split method, but rather split.

@Komaruloh 2011-06-27 10:38:15

Actualy I was asking the last one. The partName variable is filled with string indicating the key-structure to value. Your solution seems makes sense. However I may need to modify for extended depth in the data, like 4-5 level and more. And I am wondering if I can treat the array and object uniformly with this?

@Eineki 2011-06-27 10:36:48

If you need to access different nested key without knowing it at coding time (it will be trivial to address them) you can use the array notation accessor:

var part1name = someObject['part1']['name'];
var part2quantity = someObject['part2']['qty'];
var part3name1 =  someObject['part3'][0]['name'];

They are equivalent to the dot notation accessor and may vary at runtime, for example:

var part = 'part1';
var property = 'name';

var part1name = someObject[part][property];

is equivalent to

var part1name = someObject['part1']['name'];

or

var part1name = someObject.part1.name;

I hope this address your question...

EDIT

I won't use a string to mantain a sort of xpath query to access an object value. As you have to call a function to parse the query and retrieve the value I would follow another path (not :

var part1name = function(){ return this.part1.name; }
var part2quantity = function() { return this['part2']['qty']; }
var part3name1 =  function() { return this.part3[0]['name'];}

// usage: part1name.apply(someObject);

or, if you are uneasy with the apply method

var part1name = function(obj){ return obj.part1.name; }
var part2quantity = function(obj) { return obj['part2']['qty']; }
var part3name1 =  function(obj) { return obj.part3[0]['name'];}

// usage: part1name(someObject);

The functions are shorter, clearer, the interpreter check them for you for syntax errors and so on.

By the way, I feel that a simple assignment made at right time will be sufficent...

@Komaruloh 2011-06-27 10:47:38

Interesting. But in my case, the someObject is initialize yet when I assign value to part1name. I only know the structure. That is why I use string to describe the structure. And hoping to be able to use it to query my data from someObject. Thanks for sharing your thought. :)

@Eineki 2011-06-27 11:24:42

@Komaruloh : I think you would write that the object is NOT initialized yet when you create your variables. By the way I don't get the point, why can't you do the assignment at appropriate time?

@Komaruloh 2011-06-27 11:47:23

Sorry about not mentioning that someObject is not initialized yet. As for the reason, someObject is fetch via web service. And I want to have an array of header which consist of part1name, part2qty, etc. So that I could just loop through the header array and get the value I wanted based on part1name value as the 'key'/path to someObject.

Related Questions

Sponsored Content

20 Answered Questions

[SOLVED] Checking if a key exists in a JavaScript object?

76 Answered Questions

[SOLVED] How do I remove a particular element from an array in JavaScript?

  • 2011-04-23 22:17:18
  • Walker
  • 5513833 View
  • 6837 Score
  • 76 Answer
  • Tags:   javascript arrays

46 Answered Questions

[SOLVED] How to replace all occurrences of a string in JavaScript

48 Answered Questions

65 Answered Questions

[SOLVED] What is the most efficient way to deep clone an object in JavaScript?

26 Answered Questions

[SOLVED] What does "use strict" do in JavaScript, and what is the reasoning behind it?

25 Answered Questions

[SOLVED] How can I safely create a nested directory in Python?

36 Answered Questions

[SOLVED] How do I remove a property from a JavaScript object?

86 Answered Questions

[SOLVED] How do JavaScript closures work?

Sponsored Content