1

Consider you have a complex deeply nested object with 15 - 20 levels and you want to query a certain node inside it based on a field and modify a particular attribute. How can we achieve it?

Following example is contrived and the object is not as complex as my case and not as deep so please don't suggest something like obj[phoneNumbers][0].number = somethingNew

I'm using a library called jsonpath in below example to query a specific node and get its value using jsonpath expression.

var jp = require("jsonpath");

const obj = {
  firstName: "John",
  lastName: "doe",
  age: 26,
  address: {
    streetAddress: "naist street",
    city: "Nara",
    postalCode: "630-0192"
  },
  phoneNumbers: [
    {
      type: "iPhone",
      number: "0123-4567-8888"
    },
    {
      type: "home",
      number: "0123-4567-8910"
    }
  ]
};

const number = jp.query(obj, "$.phoneNumbers[0].number");
console.log(number);

// outputs: [ '0123-4567-8888' ]

Thank you.

1
  • wow! very happy to get quick responses. I will try them out and let you all know. cheers! Commented May 15, 2019 at 6:39

2 Answers 2

2

If you are okay with using lodash (npm i --save lodash) (https://lodash.com/) then its pretty easy to get or set the deeply nested value.

Get deeply nested value: (https://lodash.com/docs#get)

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'

Set deeply nested value: (https://lodash.com/docs#set)

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

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

_.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5
Sign up to request clarification or add additional context in comments.

2 Comments

I accept this answer. I believe its wiser include a mature library like lodash to do the assignment. Also, I found some cool cloning utilities (deep/shallow)
Something I never liked about lodash.set is that set(object, ['x', '0', 'y', 'z'], 5) and set(object, ['x', 0, 'y', 'z'], 5) behave identically but both depend on the input, i.e. { x: {} } and {}. Imo using a number or a string should create an array and an object respectively.
1

Here is an answer using object-scan. The main advantage over lodash is that we can specify more generic path expressions and execute the command against multiple targets at the same time. This addition in functionality comes with added complexity, and hence which one to choose depends on your requirements.

// const objectScan = require('object-scan');

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

const get = (obj, needle) => objectScan([needle], {
  abort: true,
  rtn: 'value'
})(obj);

console.log(get(object, 'a[0].b.c'));
// => 3
console.log(get(object, '**.c'));
// => 3
console.log(get(object, 'a.b.c'));
// => undefined

const set = (obj, needle, val) => objectScan([needle], {
  filterFn: ({ parent, property }) => {
    parent[property] = val;
    return true;
  },
  abort: true,
  rtn: 'bool' // returns true iff executed
})(obj);

console.log(set(object, 'a[0].*.c', 4));
// => true
console.log(object);
// => { a: [ { b: { c: 4 } } ] }
console.log(set(object, 'x[0].y.z', 5));
// => false
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>

Disclaimer: I'm the author of object-scan

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.