Why object-path Package Is Great

by — posted on

object-path opens in a new tab is a getter/setter for nested arrays and plain objects. Here we use it directly and also align our API's to it. Here's why.

First reason, it allows to get or set values by path, no matter how deep and no matter if any levels the destination path is missing.

Before optional chaining opens in a new tab, if we tried to access non-existing keys in an object, deeper than one absent level, an error would be thrown:

const { strict } = require("assert");
const assert = strict;
const objectPath = require("object-path");

// in vanilla JS, we can address first level missing keys:
const testObj = {a: "b"};
const extracted1 = testObj.x;
assert.equal(extracted1, undefined);

// but not the second:
assert.throws(() => {
const extracted2 = testObj.x.y;
}, TypeError);

// with object-path, it's fine:
const extracted3 = objectPath.get(testObj, "x.y");
assert.equal(extracted3, undefined);

// optional chaining is substitute for .get:
const extracted4 = testObj.x?.y;
assert.equal(extracted4, undefined);

While Optional chaining opens in a new tab can substitute the object-path method .get, other methods, such as .delete or .has don't have a native JS equivalent.

That's the second reason — other object-path methods would warrant a 3rd party library.

But that was plain objects. The fun part is arrays.

When we deal with arrays, object-path keeps the same notation as it did for plain objects:

const { strict } = require("assert");
const assert = strict;
const objectPath = require("object-path");

const testObj = {a: ["foo", "bar"]};

// get "bar" in vanilla JS:
const extracted1 = testObj.a[1];
assert.equal(extracted1, "bar");

// get "bar" using object-path:
const extracted2 = objectPath.get(testObj, "a.1"); // <--- notice it's a.1
assert.equal(extracted2, "bar");

This way of addressing array keys using dot, like a.1, instead of brackets a[1] is a different paradigm!

For example,

const { strict } = require("assert");
const assert = strict;
const objectPath = require("object-path");

// in theory, object keys can be number strings too:
const testObj = {"0": ["foo"]};

// let's query the "foo" using vanilla JS:
assert.equal(testObj["0"][0], "foo");

// let's query the "foo" using object-path:
assert.equal(objectPath.get(testObj, "0.0"), "foo");

The advantage of dot notation (a.1) is that we can use Array.prototype.join() to programmatically assemble the path: ["a", "1"].join(".").

Getting a[1] in vanilla JS bracket notation is not that easy.

The third reason — both object and array paths being joined with dot simplify the path assembly operations.

As a result, quite few of our packages use object-path "dot" notation:

Related packages:

📦 object-path opens in a new tab
Access deep object properties using a path