4

I have a highly nested JSON structure where I can pull out individual information like this

let result = data["a"]["b"][0]["c"]["d"][0]["e"][0]

what is an elegant way to pull the data out? Also, can the result be assigned to null or undefined when along the way any of a,b,c,d,e does not exist or when the array does not contain element 0?

Satpal
  • 132,252
  • 13
  • 159
  • 168
Se7enDays
  • 2,543
  • 2
  • 17
  • 22
  • 1
    [Optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) – ASDFGerte Sep 08 '20 at 10:38
  • My favorite solution is the `_try` function in the linked question for such long navigation, until the optional chaining operator will be supported in all the targets – Christian Vincenzo Traina Sep 08 '20 at 10:39
  • @CristianTraìna this is not necessary anymore, as it's now within the ECMA standard and is supported either through transpilation (using webpack) for older targets or directly in the JS engines (node and browsers). – zmo Sep 08 '20 at 10:42
  • @zmo I agree, but not everyone is using Babel :) some still write JavaScript for the browser. Btw the linked question contains also a reference to optional chaining operator – Christian Vincenzo Traina Sep 08 '20 at 10:44
  • I'm not disagreeing with the closure of the question as duplicate, I actually just added my vote. Though I believe the optional chaining should be preferred to the `_try` function, as now it is support by the major browsers, it's in the standard and for the backward compatibility there's babel. – zmo Sep 08 '20 at 17:18

3 Answers3

0

With latest Javascript you can use the optional chaining operator:

let result = data?.a?.b?.[0]?.c?.d?.[0]?.e?.[0]

zmo
  • 24,463
  • 4
  • 54
  • 90
  • this doesn't work on dictionary like format like in the question, this only works for nested objects – Shraneid Nov 25 '21 at 11:44
  • hum @Shraneid I'm a bit stumped by your comment. In javascript there's no such thing as a "dictionary like format", there's only objects, and property access can be done through two notations: `foo.bar` or `foo['bar']`. You don't have to take my word for it, [just have a look at the documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#syntax). – zmo Nov 25 '21 at 23:43
  • Yep you're right, sorry about that, coming from python here. However the bracket notation only works for indices and not keys (for some reason I can't understand) so data?.a?.b?.[0]?.c is a valid syntax, but data?.a?.b?.['test']?.c is not which is the source of my confusion, sorry for that – Shraneid Dec 13 '21 at 16:56
  • @Shraneid sorry to disappoint, but `obj.val?.[expr]` is valid cf [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) – zmo Dec 19 '21 at 15:45
  • it's not what I said, ```obj.["val"]?.val2``` doesn't work – Shraneid Dec 20 '21 at 17:43
  • actually, after testing it on my windows machine it seems to work here on brave and firefox, but it doesn't at work on firefox / mac, go figure. So my bad it actually works but not on all environments it would seem (obviously everything is up to date) – Shraneid Dec 20 '21 at 18:01
0

I'd say the easiest option would be an optional chaining operator or something, but I believe the right way to do this is something like JSONPath.

There might be other libraries (e.g. like jsonpath one or jsonpath-plus) which allows you to do the same.

JSONPath is XPath for JSON.

JSON Example:

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

And this is how you can get the authors of all books in the store using JSONPath expression:

$.store.book[*].author

You can use it in JavaScript:

const o = { /*...*/ },  // the 'store' JSON object from above
res1 = jsonPath(o, "$..author").toJSONString(); // all authors JSONPath expression
res2 = jsonPath(o, "$..author", {resultType:"PATH"}).toJSONString();
ikos23
  • 4,879
  • 10
  • 41
  • 60
0

You can write a function path that gets an array of properties to access and an object to read from:

  • The function is curried so you can build a function that access a specific path and reuse it with different objects
  • The reduce drills down the object until undefined or the end of the path is reached.

const path = p => o =>
  p.reduce( (res, pp) =>
              res === undefined ? res : res[pp]
          , o
          );
          
          
const abcd = path(['a', 'b', 'c', 'd']);

console.log(abcd({}));
console.log(abcd({a: 1}));
console.log(abcd({a: {b: {}}}));
console.log(abcd({a: {b: {c: {d: 42}}}}));
customcommander
  • 17,580
  • 5
  • 58
  • 84