21

Suppose I have this:

var a = { A : { AA : 1 }, B : 2 };

Is there a way for me to create a variable that could allow me to reference either AA or B? What would the syntax look like?

// I know I can do this:    
a['B']; // 2
a['A']['AA']; // 1

// something like this?
var myRef = ???;
a[myRef]; 1 or 2 depending on myRef

If not, what's a better way to get what I'm going for here?

Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
  • 2
    What is the context? Why don't you assign the value of either `a['A']['AA']` or `a['B']` to a variable? Is `myref` changing over time? But no, it is not possible that way, but [this answer](http://stackoverflow.com/a/6906859/218196) probably helps you. – Felix Kling Jan 05 '12 at 21:54
  • I was receiving a nested JSON response that I wanted to be able to sort by its various fields. – Ben Flynn Jan 05 '12 at 22:32

5 Answers5

35

Not directly.

Solution 1 - use object flattening

Flatten object, to have new object var a = { 'A.AA' : 1; B : 2 };.

See compressing object hierarchies in JavaScript or Flattening a complex json object for mvc binding to get the javascript function for it.

Soution 2 - write key-path accessor

I can see it was already addressed by Eugen. Reposted code-reviewed version:

function Leaf(obj,path) {
  path=path.split('.');
  var res=obj;
  for (var i=0;i<path.length;i++) res=res[path[i]];
  return res;
}

Solution 3 - use eval

var x = eval("a." + myRef); // x will be 1 for myRef == "A.AA", 2 for "B"

Be careful with this solution as you may introduce some security issues. It is more of the curiosity.

Community
  • 1
  • 1
Krizz
  • 11,362
  • 1
  • 30
  • 43
  • eval would also be prone to turning off optimizations and screw with performance all around. – hugomg Jan 05 '12 at 22:24
  • Thanks. This led me to my solution. I looked in google closure (which I am using) for an accessor on the Object type. They have this method: goog.object.getValueByKeys(obj, Array | number | string | undefined) so myRef can be ['A', 'AA'] or ['B'] (if I want to consistently pass an array). Anyway, thanks! – Ben Flynn Jan 05 '12 at 22:36
  • #solution #3 saved my day @Krizz Thanks – Ronak Dhoot Jun 18 '20 at 19:18
16

Since i also encounter this problem, i wrote also a one line util for this (ES6):

const leaf = (obj, path) => (path.split('.').reduce((value,el) => value[el], obj))

Example:

const objSample = { owner: { name: 'Neo' } };
const pathSample = 'owner.name';

leaf(objSample, pathSample) //'Neo'
  • 2
    Love this answer. One small tweak: const leaf = (obj, path) => (path.split('.').reduce((value,el) => value && value[el], obj)) – John in MD Nov 16 '20 at 17:52
  • Thanks! Wanted to avoid lodash in this case and this works nicely :) – Chris Apr 17 '22 at 11:18
3
function Leaf(obj,path) {
  path=path.split('.');
  var res=obj;
  for (var i=0;i<path.length;i++) obj=obj[path[i]];
  return res;
}

Leaf(a,'B')=2

Leaf(a,'A.AA')=1

Decorate with error handling etc. according to your needs.

hugomg
  • 68,213
  • 24
  • 160
  • 246
Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • -1 There were enough problems with this snippet to warrant a downvote. Besides the spelling of length and the caps on split and length, it is currently just returning the original object that was passed in. `obj=obj[path[i]]` should be `res=res[path[i]]`. http://jsfiddle.net/QuVdr/ – BNL Jan 05 '12 at 22:27
  • 2
    Duely noted - tried to vote myself down, but this isn't supported by SO. Answer not deleted for historical reasons, as it is quoted in the accepted answer. – Eugen Rieck Jan 06 '12 at 19:15
2

With lodash _.get function, you can access nested properties with dot syntax.

Node server-side example:

const _ = require('lodash'); 

let item = { a: {b:'AA'}};

 _.get(item, 'a.b');
Agustí Sánchez
  • 10,455
  • 2
  • 34
  • 25
1

Actually no, because js object are seen as property bags and doing a[X] is for accessing first level properties only...

But you could wrap the logic a['A']['AA']; // 1 in a function that does the same, like this

//WARN... no undefined check here => todo !
function _(o, path) {
  var tmp = o
  for (var i=0 ; i < path.length ; i++) {
    tmp = tmp[path[i]]
  }
  return tmp
}

var r = _(a, ['A', 'AA'])

This is pretty much the same as other answers, but the difference is when dummy boy create object property name containing dots... Like var a = {"a.a" : 3 } is valid.

Now, such problem would occurs maybe more often now with the help of IndexedDB to store anything locally...

Andy Petrella
  • 4,345
  • 26
  • 29