Traverse and transform objects by visiting every node on a recursive walk
—
When using callback functions with traversal methods (forEach, map, reduce), the this context provides extensive information about the current node and methods for modifying the traversal behavior.
The callback's this object contains the following properties that describe the current traversal state:
// Current node being visited
this.node // Current node value
this.node_ // Original node value (before modifications)
// Path information
this.path // Array of keys from root to current node
this.key // Property key of current node in its parent (undefined for root)
// Hierarchy information
this.parent // Parent context object (undefined for root)
this.parents // Array of all parent contexts
this.keys // Array of enumerable keys of current node (null for non-objects)
this.removedKeys // Object tracking keys removed from parent during traversal// Position flags
this.isRoot // boolean - Whether current node is the root
this.notRoot // boolean - Whether current node is not the root
this.isLeaf // boolean - Whether current node has no children
this.notLeaf // boolean - Whether current node has children
this.level // number - Depth of current node (0 for root)
// Traversal state
this.circular // Parent context if node creates circular reference, null otherwise
this.isFirst // boolean - Whether this is first child of parent (set during traversal)
this.isLast // boolean - Whether this is last child of parent (set during traversal)The context provides methods for modifying nodes and controlling traversal flow:
/**
* Update the current node value
* @param {*} value - New value for the node
* @param {boolean} [stopHere=false] - Whether to stop traversing this branch
*/
this.update(value, stopHere)
/**
* Remove current node (splice from arrays, delete from objects)
* @param {boolean} [stopHere=false] - Whether to stop traversing this branch
*/
this.remove(stopHere)
/**
* Delete current node (uses delete operator even on arrays)
* @param {boolean} [stopHere=false] - Whether to stop traversing this branch
*/
this.delete(stopHere)/**
* Register function to call before traversing children of current node
* @param {Function} fn - Function to call before children
*/
this.before(fn)
/**
* Register function to call after traversing children of current node
* @param {Function} fn - Function to call after children
*/
this.after(fn)
/**
* Register function to call before each child of current node
* @param {Function} fn - Function to call before each child
*/
this.pre(fn)
/**
* Register function to call after each child of current node
* @param {Function} fn - Function to call after each child
*/
this.post(fn)
/**
* Stop the entire traversal immediately
*/
this.stop()
/**
* Skip traversing children of the current node
*/
this.block()const traverse = require('traverse');
const obj = { a: { b: { c: 1 } }, d: [2, 3] };
traverse(obj).forEach(function (x) {
console.log('Path:', this.path);
console.log('Key:', this.key);
console.log('Value:', this.node);
console.log('Is leaf:', this.isLeaf);
console.log('Level:', this.level);
console.log('---');
});
// Output shows path and context info for each node:
// Path: [] Key: undefined Value: {a: {...}, d: [...]} Is leaf: false Level: 0
// Path: ['a'] Key: 'a' Value: {b: {...}} Is leaf: false Level: 1
// Path: ['a','b'] Key: 'b' Value: {c: 1} Is leaf: false Level: 2
// etc...const traverse = require('traverse');
// Transform values based on context
const obj = {
numbers: [1, 2, 3],
nested: { value: 4 }
};
traverse(obj).forEach(function (x) {
if (typeof x === 'number') {
// Double numbers at even levels, triple at odd levels
const multiplier = this.level % 2 === 0 ? 2 : 3;
this.update(x * multiplier);
}
});const traverse = require('traverse');
const obj = {
items: [1, null, 2, undefined, 3, '', 4],
nested: { a: null, b: 5, c: undefined }
};
// Remove falsy values
traverse(obj).forEach(function (x) {
if (!x && x !== 0 && x !== false) {
this.remove();
}
});const traverse = require('traverse');
const obj = {
arr: ['a', 'remove', 'b'],
obj: { keep: 1, remove: 2, keep2: 3 }
};
traverse(obj).forEach(function (x) {
if (x === 'remove' || x === 2) {
if (Array.isArray(this.parent.node)) {
this.remove(); // Splice from array
} else {
this.delete(); // Delete from object
}
}
});const traverse = require('traverse');
const obj = {
public: { data: 1, more: 2 },
private: { secret: 'hidden', confidential: 'data' },
normal: 'value'
};
// Skip traversing private sections
traverse(obj).forEach(function (x) {
if (this.key === 'private') {
console.log('Skipping private section');
this.block(); // Don't traverse children
} else {
console.log('Processing:', this.path, this.node);
}
});const traverse = require('traverse');
const obj = { a: { b: [1, 2] }, c: 3 };
traverse(obj).forEach(function (x) {
if (typeof x === 'object' && x !== null) {
this.before(function (node) {
console.log('About to traverse:', this.path);
});
this.after(function (node) {
console.log('Finished traversing:', this.path);
});
}
});const traverse = require('traverse');
const obj = { name: 'parent' };
obj.self = obj; // Create circular reference
traverse(obj).forEach(function (x) {
if (this.circular) {
console.log('Found circular reference at path:', this.path);
console.log('Points back to path:', this.circular.path);
// this.circular is the parent context that creates the cycle
}
});const traverse = require('traverse');
const config = {
database: { host: 'localhost', port: 5432 },
cache: { host: 'redis-server', port: 6379 },
api: { version: 'v1', timeout: 5000 }
};
// Update all host values to production hosts
traverse(config).forEach(function (x) {
if (this.key === 'host' && this.path.length === 2) {
const service = this.path[0]; // 'database', 'cache', etc.
this.update(`prod-${service}.example.com`);
}
});const traverse = require('traverse');
const obj = { c: 1, a: 2, b: 3 };
traverse(obj).forEach(function (x) {
if (typeof x === 'object' && x !== null) {
this.before(function () {
// Sort keys alphabetically before traversing
if (this.keys) {
this.keys.sort();
}
});
}
});const traverse = require('traverse');
let found = false;
const obj = {
level1: {
level2: {
target: 'FOUND',
other: 'data'
}
}
};
traverse(obj).forEach(function (x) {
if (x === 'FOUND') {
console.log('Found target at:', this.path);
found = true;
this.stop(); // Stop entire traversal
}
});Install with Tessl CLI
npx tessl i tessl/npm-traverse