Object Attribute Path Traverser. Safely traverse the javascript attribute tree using a text path representation. You can also check the existence of the path.
For example, I didn't like to write the following code every time... humm...
const value = {
children: {
john: {hobby: [{name: "Cycling"}, {name: "Dance"}], pet: [{type: "dog", name: "Max"}]},
tom: {hobby: [{name: "Squash"}], pet: [{type: "cat", name: "Chloe"}]}
}
};
var answer = undefined;
if (value.children) {
if (value.children.john) {
if (value.children.john.hobby) {
if (Array.isArray(value.children.john.hobby)) {
if (value.children.john.hobby.length >= 2) {
if (value.children.john.hobby[1].name) {
answer = value.children.john.hobby[1].name;
}
}
}
}
}
}
or
answer = value?.children?.john?.hobby[1]?.name;The ugly thing above is nice if you can write it like this, right?
const {AttrPath}: any = require("attrpath");
var answer = AttrPath.traverse(value, ".children.john.hobby[1].name");Safely traverse the object path using the given path string. Also, an array may be included in the middle of the path.
npm install atttrpathNo modules depend on it.
const {AttrPath}: any = require("attrpath");
// or
import {AttrPath} from 'attrpath';result = AttrPath.traverse(object, path [default_value]);| params | meaning |
|---|---|
| object: any | Target Object. |
| path: string | Traverse path. The beginning of the path is "." or "[". |
| e.g. | ".cat.eye.left", ".dog['leg'][1].pad" , etc... |
| default_value: any | The value to return if there is no corresponding value in the object path. default is "undefined". |
| default_value: function | If you give a function, give the traverse result to the first argument of the function. |
| result | meaning |
|---|---|
| result: any | Objects obtained as a result of traverse. |
result = AttrPath.update(object, path , value);| params | meaning |
|---|---|
| object: any | Target Object. |
| path: string | Traverse path. The beginning of the path is "." or "[". |
| e.g. | ".cat.eye.left", ".dog['leg'][1].pad" , etc... |
| value: any | The value to update if the path exists. |
| result | meaning |
|---|---|
| result: boolean | Has an update. |
result = AttrPath.is_valid(path);| params | meaning |
|---|---|
| path: string | Traverse path. |
| result | meaning |
|---|---|
| result: boolean | path is grammatically correct? |
If the result is Undefined, the default value is returned.
const {AttrPath} = require('attrpath');
AttrPath.traverse({}, '.path', 1);const {AttrPath}: any = require("attrpath");
const value = {
children: {
john: {
hobby: [{name: "Cycling"}, {name: "Dance"}],
pet: [{type: "dog", name: "Max"}]
},
tom: {
hobby: [{name: "Squash"}],
pet: [{type: "cat", name: "Chloe"}]
}
}
};
console.log(AttrPath.traverse(value, '.children.john.hobby[0].name'))
> "Max"
console.log(AttrPath.traverse(value, '.children.john.hobby[1].name'))
> undefined
const Default = (n:any) => {
console.log(n);
}
AttrPath.traverse(value, '.children.john.hobby[0].name', Default)
> "Max"
console.log(AttrPath.is_valid('.children.john.hobby[0].name'))
> true
console.log(AttrPath.is_valid('.children.john.hobby[0]..name'))
> falseclass Klass {
member = "name";
Member() {
return AttrPath.traverse(this, '.member');
}
}
const klass = new Klass();
console.log(klass.Member())
> "name"class ParentKlass {
member = "name";
}
class SubKlass extends ParentKlass {
Member() {
return AttrPath.traverse(this, '.member');
}
}
const sub_klass = new SubKlass();
console.log(sub_klass.Member())
> "name" const value = {
children: {
john: {
hobby: [{name: "Cycling"}, {name: "Dance"}],
pet: [{type: "dog", name: "Max"}]
},
tom: {
hobby: [{name: "Squash"}],
pet: [{type: "cat", name: "Chloe"}]
}
}
};import {AttrPath} from 'attrpath';
AttrPath.traverse(value, '.children')
AttrPath.is_valid('.children["john"].hobby[1].name')const {AttrPath} = require('attrpath');
AttrPath.traverse(value, '.children');
AttrPath.is_valid('.children["john"].hobby[1].name')The original value can be an array.
const {AttrPath} = require('attrpath');
AttrPath.traverse([1], '[0]');Returns Undefined if the original value is not an object.
const {AttrPath} = require('attrpath');
AttrPath.traverse(null, '.path');
AttrPath.traverse(undefined, '.path');
AttrPath.traverse(false, '.path');
AttrPath.traverse(true, '.path');
AttrPath.traverse(NaN, '.path');
AttrPath.traverse(Infinity, '.path');
AttrPath.traverse(0, '.path');
AttrPath.traverse(-1, '.path');
AttrPath.traverse("", '.path');
AttrPath.traverse("1", '.path');
AttrPath.traverse([1], '.path');
AttrPath.traverse({}, '.path');Note that the result object is just the argument object, so mutating the result object has the side effect of modifying the argument object. This side effect can actually be useful.
const before = [{name: "Dance"}];
AttrPath.traverse(before, '[0]').name = "Breaking";
console.log(before);
[{name: "Breaking"}]update Method impl.
Fixed to work correctly when the key contains ".".
const value = {
"children.john": {
hobby: [{name: "Cycling"}, {name: "Dance"}],
pet: [{type: "dog", name: "Max"}]
}
};
AttrPath.traverse(value, "['children.john']");npm run build # Build both CommonJS and ESM modules
npm run build:common # Build CommonJS module only
npm run build:esm # Build ESM module onlynpm test # Run all tests with Jest
npm run test # Same as abovenpm run doc # Generate TypeDoc documentation in docs/typedoc/npm run prepare # Automatically runs build (executed on npm install)AttrPath is a TypeScript library for safely traversing JavaScript object attribute paths using string notation. The core architecture consists of:
AttrPath (src/index.ts) - Main API class with static methods:
traverse(target, path, default_value?)- Safely navigate object pathsupdate(target, path, value)- Update values at specific pathsis_valid(path)- Validate path syntax
Parser System (src/parser.ts):
AttributeParser- Main parser for attribute path stringsFormulaParser- Extended parser for formula expressionsBaseParser- Shared parsing functionalityTokenType- Enum defining token types for parsing
Handler System (src/handler.ts):
ValueHandler- Extracts values during path traversalUpdater- Updates values during path traversalBaseHandler- Abstract base for handler implementations
Stream Processing (src/stream.ts):
ParserStream- Character stream processing for path parsing
Utilities (src/base.ts):
- Type checking utilities (
isNumber,isContainer,isValue)
The library supports complex path expressions:
- Object properties:
.propertyor['property'] - Array indices:
[0]or[index] - Mixed paths:
.children.john.hobby[1].name - Keys with dots:
['children.john']
Dual module support:
- CommonJS: Built to
dist/usingtsconfig.json - ESM: Built to
dist/esm/usingtsconfig.esm.json
Both builds include TypeScript declarations and source maps.
- Framework: Jest with ts-jest transformer
- Coverage: Enabled with output to
docs/coverage/ - Test Files: Located in
src/test.ts - Configuration:
jest.config.ts
The test suite covers the core API, parsing logic, and edge cases for safe traversal.
The results of using objects, arrays, and symbols as keys are unknown.
See demo.md for unclear cases.
"AttrPath" is under MIT license.