npm i object-traversal
npm run benchmark
)traversalOpts
for even more speed, traversal order, maxDepth and more.import { traverse } from 'object-traversal';
traverse(object, callback, opts?);
Any instance of javascript object, cyclic or otherwise.
A function that will be called once for each node in the provided root object
, including the root object
itself.
The callback
function has the following signature:
// Callback function signature
export type TraversalCallback = (context: TraversalCallbackContext) => any;
// Callback context
export type TraversalCallbackContext = {
parent: ArbitraryObject | null; // parent is null when callback is being called on the root `object`
key: string | null; // key is null when callback is being called on the root `object`
value: any;
meta: {
nodePath?: string | null;
visitedNodes: WeakSet<ArbitraryObject>;
depth: number;
};
};
An optional configuration object. See below for the available options and their default values.
export type TraversalOpts = {
/**
* Default: 'depth-first'
*/
traversalType?: 'depth-first' | 'breadth-first';
/**
* Traversal stops when the traversed node count reaches this value.
*
* Default: Number.Infinity
*/
maxNodes?: number;
/**
* If set to `true`, prevents infinite loops by not re-visiting repeated nodes.
*
* Default: true
*/
cycleHandling?: boolean;
/**
* The maximum depth that must be traversed.
*
* Root object has depth 0.
*
* Default: Number.Infinity
*/
maxDepth?: number;
/**
* If true, traversal will stop as soon as the callback returns a truthy value.
*
* This is useful for search use cases, where you typically want to skip traversing the remaining nodes once the target is found.
*
* Default: false
*/
haltOnTruthy?: boolean;
/**
* The string to be used as separator for the `meta.nodePath` segments.
*
* Set to null if you wish to turn off `meta.nodePath` to increase traversal speed.
*
* Default: '.'
*/
pathSeparator?: string | null;
};
function double({ parent, key, value, meta }) {
if (typeof value === 'number') {
parent[key] = value * 2;
}
}
traverse(exampleObject, double);
console.log(exampleObject);
// {
// name: 'Hello World!',
// age: 2,
// accounts: { checking: 4, savings: 6 },
// friends: 8
// }
const numbersOver25 = [];
function collectOver25({ parent, key, value, meta }) {
if (key === 'age' && value > 25) {
numbersOver25.push(value);
}
}
traverse(network, collectOver25);
console.log(numbersOver25);
// [ 52, 42, 33 ]
const pathsToPeopleNamedJohn = [];
function callback({ parent, key, value, meta }) {
if (value.name && value.name.startsWith('John')) {
pathsToPeopleNamedJohn.push(meta.nodePath);
}
}
traverse(network, callback);
console.log(pathsToPeopleNamedJohn);
// [ 'friends.0', 'friends.1.friends.0' ]
import { getNodeByPath } from 'object-traversal';
const firstFriend = getNodeByPath(network, 'friends.0');
console.log(firstFriend);
// { name: 'John Doe', age: 25, friends: [] }
let names = [];
function getName({ parent, key, value, meta }) {
if (value.name) {
names.push(value.name);
}
}
traverse(network, getName, { traversalType: 'breadth-first' });
console.log(names);
// [ 'Person 1', 'Person 2', 'Person 3', 'Person 4' ]