Accessing an object property with a dynamically-computed name
Asked Answered
D

21

975

I'm trying to access a property of an object using a dynamic name. Is this possible?

const something = { bar: "Foobar!" };
const foo = 'bar';
something.foo; // The idea is to access something.bar, getting "Foobar!"
Despatch answered 22/11, 2010 at 11:23 Comment(1)
I
1293

There are two ways to access properties of an object:

  • Dot notation: something.bar
  • Bracket notation: something['bar']

The value between the brackets can be any expression. Therefore, if the property name is stored in a variable, you have to use bracket notation:

var something = {
  bar: 'foo'
};
var foo = 'bar';

// both x = something[foo] and something[foo] = x work as expected
console.log(something[foo]);
console.log(something.bar)
Inocenciainoculable answered 22/11, 2010 at 11:25 Comment(12)
careful with this: javascript compilers will error here since they dont rename strings but they do rename object propertiesSober
Some more info on why this is possible: JS objects are associative arrays, that's why. Further Reading: quirksmode.org/js/associative.html #14031868Aconcagua
@dotnetguy No they are not. Arrays are objects that inherit from the plain JS object prototype and therefore you can add properties a go-go like any plain object. The 'associative' behaviour is more object-like than array like. You can't iterate the 'associative' version by simple index so it is not displaying array-like behaviour. You can define your 'associative' array as {} or [] and treat it the same in either case as far as random property access is concerned.Outsole
@VanquishedWombat Not sure what your objection pertains to? I did not say that JS Objects are arrays?Aconcagua
as a reference to the correct answer , ReferenceNordic
Is it possible to take this further and get a property of an object that is a property of my base object? I have a 'car' object and I want to know the drivers name. my car has a property of Driver. that driver object has a name property. ie. car['Driver.Name']? <- this doesn work for me. thanksFabrianna
@Fabrianna simply do car.Driver.NameImmobile
When use to access properties using bracket notation its tslist issue throwing warning Generic Object Injection Sink security/detect-object-injectionOppugnant
In TypeScipt, this works if first casted to any. (obj as any).x[foo]. Even better solution is to user InterfacesRemark
this is working for me but i am getting Identifier expected. eslint error when using it. any ideas?Hypethral
ECMAScript 2020 optional chaining (safe navigation) works like this: object?.property for a property literally called property; object?.[property] for a property whose name is contained in the variable property. Note that it’s not object?[property]. Also, for method calls, the syntax is object?.(argument). (Recent relevant question: Logging an interpolated value in javascript).Misprision
Please restrain from using foo-bar in the future... This explanation is so obfuscated.Discrete
H
196

This is my solution:

function resolve(path, obj) {
    return path.split('.').reduce(function(prev, curr) {
        return prev ? prev[curr] : null
    }, obj || self)
}

Usage examples:

resolve("document.body.style.width")
// or
resolve("style.width", document.body)
// or even use array indexes
// (someObject has been defined in the question)
resolve("part.0.size", someObject) 
// returns null when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})
Hilly answered 26/7, 2017 at 8:57 Comment(6)
Excellent answer, see also: #37511140Wash
You inspired me to create an enhanced version that allows bracket notation & property names with spaces as well as validating the inputs: it.knightnet.org.uk/kb/node-js/get-propertiesWash
I love this solution. However I'm trying to modify the values in the original object, it seems your function returns a sub copy of the object. Is it possible to change it so that modifying the returned object modifies the original ?Resistive
I'd also like to see the "set value" version of this.Derwon
Note that modern JS engines now support ?., allowing you to achieve the same effect like this: document.body?.style?.width. No helper function necessary. More infoCasaubon
optional chaining (?.) doesn't get rid of the need for the helper function, since you still need to traverse the chain from a variable. However it would allow you to simplify the reduce function a bit with return prev?.[curr]. Although note the slight difference that this will return undefined if prev doesn't exist instead of null.Loralyn
S
65

In javascript we can access with:

  • dot notation - foo.bar
  • square brackets - foo[someVar] or foo["string"]

But only second case allows to access properties dynamically:

var foo = { pName1 : 1, pName2 : [1, {foo : bar }, 3] , ...}

var name = "pName"
var num  = 1;

foo[name + num]; // 1

// -- 

var a = 2;
var b = 1;
var c = "foo";

foo[name + a][b][c]; // bar
Socha answered 1/7, 2014 at 15:40 Comment(1)
I'm staring at 2,000 lines of if statements because the previous dev didn't use square brackets, and statically accessed object properties by dot notation. It's for an approval process app that has 7 different approvers and the steps are all the same. /ripAlgia
W
43

Following is an ES6 example of how you can access the property of an object using a property name that has been dynamically generated by concatenating two strings.

var suffix = " name";

var person = {
    ["first" + suffix]: "Nicholas",
    ["last" + suffix]: "Zakas"
};

console.log(person["first name"]);      // "Nicholas"
console.log(person["last name"]);       // "Zakas"

This is called computed property names

Wherefore answered 2/8, 2016 at 19:46 Comment(0)
G
27

You can achieve this in quite a few different ways.

let foo = {
    bar: 'Hello World'
};

foo.bar;
foo['bar'];

The bracket notation is specially powerful as it let's you access a property based on a variable:

let foo = {
    bar: 'Hello World'
};

let prop = 'bar';

foo[prop];

This can be extended to looping over every property of an object. This can be seem redundant due to newer JavaScript constructs such as for ... of ..., but helps illustrate a use case:

let foo = {
    bar: 'Hello World',
    baz: 'How are you doing?',
    last: 'Quite alright'
};

for (let prop in foo.getOwnPropertyNames()) {
    console.log(foo[prop]);
}

Both dot and bracket notation also work as expected for nested objects:

let foo = {
    bar: {
        baz: 'Hello World'
    }
};

foo.bar.baz;
foo['bar']['baz'];
foo.bar['baz'];
foo['bar'].baz;

Object destructuring

We could also consider object destructuring as a means to access a property in an object, but as follows:

let foo = {
    bar: 'Hello World',
    baz: 'How are you doing?',
    last: 'Quite alright'
};

let prop = 'last';
let { bar, baz, [prop]: customName } = foo;

// bar = 'Hello World'
// baz = 'How are you doing?'
// customName = 'Quite alright'
Gunpowder answered 8/3, 2017 at 11:30 Comment(0)
K
18

You can do it like this using Lodash get

_.get(object, 'a[0].b.c');
Kynthia answered 6/9, 2018 at 6:36 Comment(2)
There are many situations, such as deep nested object lookups, where this is the only option.Arbitrate
Not enough jQuery.Humo
H
15

UPDATED

Accessing root properties in an object is easily achieved with obj[variable], but getting nested complicates things. Not to write already written code I suggest to use lodash.get.

Example

// Accessing root property
var rootProp = 'rootPropert';
_.get(object, rootProp, defaultValue);

// Accessing nested property
var listOfNestedProperties = [var1, var2];
_.get(object, listOfNestedProperties);

Lodash get can be used in different ways, the documentation lodash.get

Harod answered 22/6, 2015 at 8:10 Comment(9)
It's best to avoid using eval whenever possible. #87013Epigene
Using eval for something as trivial as accessing properties is plain overkill and hardly advisable under any circumstance. What's "trouble"? obj['nested']['test'] works very well and doesn't require you to embed code in strings.Errolerroll
Question is how to access property dynamically, and you have given example where you use everything static. Give me example how can you access 3d level child dynamically simpler (with no overkill) and I will delete my answer.Harod
eval is three times slower or more, I wouldn't recommend this to newbies because it might teach them bad habits. I use obj['nested']['value'] - remember kids, eval is evil!Expiration
For the record, eval is slow but there's no security issue with eval in this example.Drubbing
@MrBr You use square bracket notation when you have a variable which has the name. var key = isFullMoon() ? 'werewolf' : 'human', personState = state[key]; If isFullMoon() returns true, it puts state.werewolf into personState, otherwise it puts state.human into personState, assuming state has a human property and a werewolf property. Of course, this example is necessarily oversimplified, in more realistic code, key is more unpredictable/dynamic.Innutrition
@Epigene He's now the only want to bring Lodash _.get to the table. I think this answer deserves now upvotes instead of downvotes. It may be overkill, but it's good to know it exists.Scute
Thank you for introducing lodash for this. I came here by google looking for a method to set a value deep in an object, and used their _.set method (which is identical to above but with the extra arguement for the value to set).Gradate
What if I want to throw an error if an intermediate property is missing (but allow the leaf property to be undefined)? Can I leverage lodash in that case?Isodynamic
C
11

To access a property dynamically, simply use square brackets [] as follows:

const something = { bar: "Foobar!" };
const userInput = 'bar';
console.log(something[userInput])

The problem

There's a major gotchya in that solution! (I'm surprised other answers have not brought this up yet). Often you only want to access properties that you've put onto that object yourself, you don't want to grab inherited properties.

Here's an illustration of this issue. Here we have an innocent-looking program, but it has a subtle bug - can you spot it?

const agesOfUsers = { sam: 16, sally: 22 }
const username = prompt('Enter a username:')
if (agesOfUsers[username] !== undefined) {
  console.log(`${username} is ${agesOfUsers[username]} years old`)
} else {
  console.log(`${username} is not found`)
}

When prompted for a username, if you supply "toString" as a username, it'll give you the following message: "toString is function toString() { [native code] } years old". The issue is that agesOfUsers is an object, and as such, automatically inherits certain properties like .toString() from the base Object class. You can look here for a full list of properties that all objects inherit.

Solutions

  1. Use a Map data structure instead. The stored contents of a map don't suffer from prototype issues, so they provide a clean solution to this problem.

const agesOfUsers = new Map()
agesOfUsers.set('sam', 16)
agesOfUsers.set('sally', 2)
console.log(agesOfUsers.get('sam')) // 16

  
  1. Use an object with a null prototype, instead of the default prototype. You can use Object.create(null) to create such an object. This sort of object does not suffer from these prototype issues, because you've explicitly created it in a way that it does not inherit anything.

const agesOfUsers = Object.create(null)
agesOfUsers.sam = 16
agesOfUsers.sally = 22;
console.log(agesOfUsers['sam']) // 16
console.log(agesOfUsers['toString']) // undefined - toString was not inherited

  
  1. You can use Object.hasOwn(yourObj, attrName) to first check if the dynamic key you wish to access is directly on the object and not inherited (learn more here). This is a relatively newer feature, so check the compatibility tables before dropping it into your code. Before Object.hasOwn(yourObj, attrName) came around, you would achieve this same effect via Object.prototype.hasOwnProperty.call(yourObj, attrName). Sometimes, you might see code using yourObj.hasOwnProperty(attrName) too, which sometimes works but it has some pitfalls that you can read about here.

// Try entering the property name "toString",
// you'll see it gets handled correctly.
const user = { name: 'sam', age: 16 }
const propName = prompt('Enter a property name:')
if (Object.hasOwn(user, propName)) {
  console.log(`${propName} = ${user[propName]}`)
} else {
  console.log(`${propName} is not found`)
}
  1. If you know the key you're trying to use will never be the name of an inherited property (e.g. maybe they're numbers, or they all have the same prefix, etc), you can choose to use the original solution.
Casaubon answered 13/7, 2021 at 23:26 Comment(0)
E
6

I came across a case where I thought I wanted to pass the "address" of an object property as data to another function and populate the object (with AJAX), do lookup from address array, and display in that other function. I couldn't use dot notation without doing string acrobatics so I thought an array might be nice to pass instead. I ended-up doing something different anyway, but seemed related to this post.

Here's a sample of a language file object like the one I wanted data from:

const locs = {
  "audioPlayer": {
    "controls": {
      "start": "start",
      "stop": "stop"
    },
    "heading": "Use controls to start and stop audio."
  }
}

I wanted to be able to pass an array such as: ["audioPlayer", "controls", "stop"] to access the language text, "stop" in this case.

I created this little function that looks-up the "least specific" (first) address parameter, and reassigns the returned object to itself. Then it is ready to look-up the next-most-specific address parameter if one exists.

function getText(selectionArray, obj) {
  selectionArray.forEach(key => {
    obj = obj[key];
  });
  return obj;
}

usage:

/* returns 'stop' */
console.log(getText(["audioPlayer", "controls", "stop"], locs)); 

/* returns 'use controls to start and stop audio.' */
console.log(getText(["audioPlayer", "heading"], locs)); 
Epigene answered 25/2, 2020 at 19:19 Comment(0)
A
3

Others have already mentioned 'dot' and 'square' syntaxes so I want to cover accessing functions and sending parameters in a similar fashion.

Code jsfiddle

var obj = {method:function(p1,p2,p3){console.log("method:",arguments)}}

var str = "method('p1', 'p2', 'p3');"

var match = str.match(/^\s*(\S+)\((.*)\);\s*$/);

var func = match[1]
var parameters = match[2].split(',');
for(var i = 0; i < parameters.length; ++i) {
  // clean up param begninning
    parameters[i] = parameters[i].replace(/^\s*['"]?/,'');
  // clean up param end
  parameters[i] = parameters[i].replace(/['"]?\s*$/,'');
}

obj[func](parameters); // sends parameters as array
obj[func].apply(this, parameters); // sends parameters as individual values
Ablate answered 13/11, 2016 at 17:37 Comment(0)
C
3

ES5 // Check Deeply Nested Variables

This simple piece of code can check for deeply nested variable / value existence without having to check each variable along the way...

var getValue = function( s, context ){
    return Function.call( context || null, 'return ' + s )();
}

Ex. - a deeply nested array of objects:

a = [ 
    {
      b : [
          {
             a : 1,
             b : [
                 {
                    c : 1,
                    d : 2   // we want to check for this
                 }
             ]
           }
      ]
    } 
]

Instead of :

if(a && a[0] && a[0].b && a[0].b[0] && a[0].b[0].b && a[0].b[0].b[0] && a[0].b[0].b[0].d && a[0].b[0].b[0].d == 2 )  // true

We can now :

if( getValue('a[0].b[0].b[0].d') == 2 ) // true

Cheers!

Ceramic answered 21/8, 2019 at 7:9 Comment(6)
If the solution is to use eval, you just created a million other problems.Slattern
@RodrigoLeite ok, so it wouldn't be a problem to give at least one...Ceramic
Here you go :) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…!Slattern
@RodrigoLeite I have read it, and updated the solution to use Function insteadCeramic
The correct approach for this in ECMAScript 2020 is using optional chaining: if(a?.[0]?.b?.[0]?.b?.[0]?.d === 2){}.Misprision
@Ceramic - Function.call() is still a form of eval (you're still evaluating Javascript as a string, just without explicitly calling the eval function), this hasn't fixed anything.Casaubon
S
2

demo object example

let obj = {
    name: {
        first_name: "Bugs",
        last_name: "Founder",
        role: "Programmer"
    }
}

dotted string key for getting the value of

let key = "name.first_name"

Function

const getValueByDottedKeys = (obj, strKey)=>{
    let keys = strKey.split(".")
    let value = obj[keys[0]];   
    for(let i=1;i<keys.length;i++){
        value = value[keys[i]]
    }
    return value
}

Calling getValueByDottedKeys function

value = getValueByDottedKeys(obj, key)
console.log(value)

output

Bugs

const getValueByDottedKeys = (obj, strKey)=>{
    let keys = strKey.split(".")
    let value = obj[keys[0]];   
    for(let i=1;i<keys.length;i++){
        value = value[keys[i]]
    }
    return value
}

let obj = {
    name: {
        first_name: "Bugs",
        last_name: "Founder",
        role: "Programmer"
    }
}

let key = "name.first_name"

value = getValueByDottedKeys(obj, key)
console.log(value)
Slope answered 9/12, 2022 at 3:3 Comment(0)
A
0

I asked a question that kinda duplicated on this topic a while back, and after excessive research, and seeing a lot of information missing that should be here, I feel I have something valuable to add to this older post.

  • Firstly I want to address that there are several ways to obtain the value of a property and store it in a dynamic Variable. The first most popular, and easiest way IMHO would be:
let properyValue = element.style['enter-a-property'];

however I rarely go this route because it doesn't work on property values assigned via style-sheets. To give you an example, I'll demonstrate with a bit of pseudo code.

 let elem = document.getElementById('someDiv');
 let cssProp = elem.style['width'];

Using the code example above; if the width property of the div element that was stored in the 'elem' variable was styled in a CSS style-sheet, and not styled inside of its HTML tag, you are without a doubt going to get a return value of undefined stored inside of the cssProp variable. The undefined value occurs because in-order to get the correct value, the code written inside a CSS Style-Sheet needs to be computed in-order to get the value, therefore; you must use a method that will compute the value of the property who's value lies within the style-sheet.

  • Henceforth the getComputedStyle() method!
function getCssProp(){
  let ele = document.getElementById("test");
  let cssProp = window.getComputedStyle(ele,null).getPropertyValue("width");
}

W3Schools getComputedValue Doc This gives a good example, and lets you play with it, however, this link Mozilla CSS getComputedValue doc talks about the getComputedValue function in detail, and should be read by any aspiring developer who isn't totally clear on this subject.

  • As a side note, the getComputedValue method only gets, it does not set. This, obviously is a major downside, however there is a method that gets from CSS style-sheets, as well as sets values, though it is not standard Javascript. The JQuery method...
$(selector).css(property,value)

...does get, and does set. It is what I use, the only downside is you got to know JQuery, but this is honestly one of the very many good reasons that every Javascript Developer should learn JQuery, it just makes life easy, and offers methods, like this one, which is not available with standard Javascript. Hope this helps someone!!!

Apollus answered 17/1, 2020 at 2:40 Comment(1)
The content here is a duplicate of many other questions and their answers (e.g., JavaScript get Styles, myDiv.style.display returns blank when set in master stylesheet) Those questions and answers should be referred to rather than yet another answer saying the same thing being created.Novanovaculite
K
0

For anyone looking to set the value of a nested variable, here is how to do it:

const _ = require('lodash'); //import lodash module

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

_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4

Documentation: https://lodash.com/docs/4.17.15#set

Also, documentation if you want to get a value: https://lodash.com/docs/4.17.15#get

Kiloliter answered 21/5, 2020 at 15:40 Comment(0)
F
0

You can do dynamically access the property of an object using the bracket notation. This would look like this obj[yourKey] however JavaScript objects are really not designed to dynamically updated or read. They are intended to be defined on initialisation.

In case you want to dynamically assign and access key value pairs you should use a map instead.

const yourKey = 'yourKey';

// initialise it with the value
const map1 = new Map([
  ['yourKey', 'yourValue']
]);

// initialise empty then dynamically assign
const map2 = new Map();
map2.set(yourKey, 'yourValue');

console.log(map1.get(yourKey));
console.log(map2.get(yourKey));
Fonville answered 30/8, 2022 at 8:56 Comment(0)
J
0

I bumped into the same problem, but the lodash module is limited when handling nested properties. I wrote a more general solution following the idea of a recursive descendent parser. This solution is available in the following Gist:

Recursive descent object dereferencing

Jepum answered 11/1, 2023 at 2:57 Comment(0)
A
0

Here is one way to access object property dynamically, Case is to access JSON Object based on localstorage data {preferredIndustry= foodItemsFeatured}

data.foodItemsFeatured to be accessed

enter image description here

let storageData = this.data; // JSON Response
let preferredData = localStorage.getItem('preferredIndustry'); //foodItemsFeatured

/* Dynamically access Object's property */
this.preferredFeaturedItems = this.data[preferredData as keyof typeof storageData];
Alumnus answered 10/10, 2023 at 9:33 Comment(0)
P
0

If you want to avoid the ES6 {[prop]:val} method and be able to use it in an arrow function:

var obj = {a:1}, prop = "b", val = 2;
console.log(JSON.stringify(obj)); // {"a":1}
Object.defineProperty(obj, prop, {value:val, writable:true,enumerable:true,configurable:true});
console.log(JSON.stringify(obj)); // {"a":1,"b":2};

Arrow function:

var set = (obj, prop, val) => Object.defineProperty(obj, prop, {value:val, writable:true,enumerable:true,configurable:true});
set(obj, "c", 3);
console.log(JSON.stringify(obj));// {"a":1,"b":2, "c":3};
Precritical answered 9/3 at 20:9 Comment(0)
S
-1

Finding Object by reference without, strings, Note make sure the object you pass in is cloned , i use cloneDeep from lodash for that

if object looks like

const obj = {data: ['an Object',{person: {name: {first:'nick', last:'gray'} }]

path looks like

const objectPath = ['data',1,'person',name','last']

then call below method and it will return the sub object by path given

const child = findObjectByPath(obj, objectPath)
alert( child) // alerts "last"


const findObjectByPath = (objectIn: any, path: any[]) => {
    let obj = objectIn
    for (let i = 0; i <= path.length - 1; i++) {
        const item = path[i]
        // keep going up to the next parent
        obj = obj[item] // this is by reference
    }
    return obj
}
Sentiment answered 28/8, 2020 at 4:35 Comment(0)
A
-1

You can use getter in Javascript

getter Docs

Check inside the Object whether the property in question exists, If it does not exist, take it from the window

const something = {
    get: (n) => this.n || something.n || window[n]
};
Aswarm answered 14/10, 2021 at 12:15 Comment(0)
F
-4

You should use JSON.parse, take a look at https://www.w3schools.com/js/js_json_parse.asp

const obj = JSON.parse('{ "name":"John", "age":30, "city":"New York"}')
console.log(obj.name)
console.log(obj.age)
Facies answered 14/6, 2017 at 21:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.