-
-
Save tushar-borole/567c1d22ca8d5498cbc0 to your computer and use it in GitHub Desktop.
Object tree traversal in javascript (with lodash)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var data = { | |
"name": "root", | |
"contents": [ | |
{ | |
"name": "A", | |
"contents": [ | |
{ | |
"name": "fileA1", | |
"contents": [] | |
} | |
] | |
}, | |
{ | |
"name": "B", | |
"contents": [ | |
{ | |
"name": "dirB1", | |
"contents": [ | |
{ | |
"name": "fileBB1", | |
"contents": [] | |
} | |
] | |
}, | |
{ | |
"name": "fileB1", | |
"contents": [] | |
} | |
] | |
} | |
] | |
}; | |
traverse(data); | |
function traverse(obj) { | |
_.forIn(obj, function (val, key) { | |
console.log(key, val); | |
if (_.isArray(val)) { | |
val.forEach(function(el) { | |
if (_.isObject(el)) { | |
traverse(el); | |
} | |
}); | |
} | |
if (_.isObject(key)) { | |
traverse(obj[key]); | |
} | |
}); | |
} |
iterate(obj[vKey]);
iterate
is not defined. deepTraversal
perhaps .... otherwise there is no recursion ;)
Here's a funny one which works for serializable/deserializable types:
function traverse(obj, f) {
return JSON.parse(JSON.stringify(obj), f);
}
JSON.parse calls f on every key-value pair, and what you return is what the new value will be.
Here's a funny one which works for serializable/deserializable types:
function traverse(obj, f) { return JSON.parse(JSON.stringify(obj), f); }
JSON.parse calls f on every key-value pair, and what you return is what the new value will be.
👍👍👍
export const DELETE = Symbol('DELETE');
/**
* Traverse an object and apply a transformation function to each value.
* The transformation function is called with the value, the key and the key path.
*
* The transformation function can return a new value or the same value.
* If the transformation function returns a new value, the value in the object is replaced with the new value.
*
* The transformation function can return an object.
* If the transformation function returns an object, the object is traversed as well.
*
* Warning: The transformation could loop infinitely if it always returns an object.
*
* @param obj { object } - the object to traverse
* @param transform { (value: unknown, key: string, keyPath: string) => unknown } - a function that transforms the value, the key and the key path
*/
export async function traverse(obj, transform) {
/**
* @type { object[] }
*/
const objectStack = [obj];
/**
* @type { string[] }
*/
const keysStack = []
while (objectStack.length > 0) {
const obj = objectStack.pop();
if (obj) {
for (const key in obj) {
keysStack.push(key);
const keyPath = keysStack.join('.');
const value = obj[key];
const transformed = transform(value, key, keyPath);
if(transformed === DELETE){
delete obj[key];
keysStack.pop();
continue;
}
if (transformed !== value) {
obj[key] = transformed;
}
if (typeof transformed === 'object' && transformed !== null) {
objectStack.push(transformed);
} else {
keysStack.pop();
}
}
}
}
}
/**
* Traverse an object and apply a transformation function to each value.
* The transformation function is called with the value, the key and the key path.
*
* The transformation function can return a new value or the same value.
* If the transformation function returns a new value, the value in the object is replaced with the new value.
*
* The transformation function can return an object.
* If the transformation function returns an object, the object is traversed as well.
*
* The transformation function can return a promise.
* If the transformation function returns a promise, the promise is awaited and the resolved value is used.
*
* Warning: The transformation could loop infinitely if it always returns an object.
*
* @param obj { object } - the object to traverse
* @param transform { (value: unknown, key: string, keyPath: string) => unknown | Promise<unknown> } - a function that transforms the value, the key and the key path
* @returns
**/
export async function traverseAsync(obj, transform) {
/**
* @type { object[] }
*/
const objectStack = [obj];
/**
* @type { string[] }
*/
const keysStack = []
while (objectStack.length > 0) {
const obj = objectStack.pop();
if (obj) {
for (const key in obj) {
keysStack.push(key);
const keyPath = keysStack.join('.');
const value = obj[key];
const awaitedValue = await value;
const transformed = await transform(awaitedValue, key, keyPath);
if(transformed === DELETE){
delete obj[key];
keysStack.pop();
continue;
}
if(value !== transformed){
obj[key] = transformed;
}
if (typeof transformed === 'object' && transformed !== null) {
objectStack.push(transformed);
} else {
keysStack.pop();
}
}
}
}
}
const obj = { a: 1, b: { c: [{ a: 2 }, { d: 20}] } }
traverse(obj, (value, key, keyPath) => {
if(keyPath === 'a'){
return DELETE;
}
return value
})
obj
const asyncObj = { a: 1, b: { c: Promise.resolve([{ a: 2 }]) } }
traverseAsync(asyncObj, (value, key, keyPath) => {
if(keyPath === 'a'){
return DELETE;
}
return value
}).then(() => {
asyncObj
})
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Pure JS function to execute callback on every match