const isArray = value => Array.isArray(value);
const isObject = value => Object.prototype.toString.call(value) === '[object Object]';
function* walkTree(collection) {
function* visit(value, path, parentNode) {
const node = Object.create({ parentNode });
node.path = path;
node.value = value;
yield node;
if (isArray(value)) {
for (let i = 0, len = value.length; i < len; i++) {
yield* visit(value[i], path.concat(i), node);
}
} else if (isObject(value)) {
for (const key in value) {
if (value.hasOwnProperty(key)) {
yield* visit(value[key], path.concat(key), node);
}
}
}
}
yield* visit(collection, [], null);
}
const treeWalker = walkTree({ a: { b: { c: [1, 2] } } });
for (const node of treeWalker) {
console.log(node, node.__proto__);
}
Output:
{ path: [], value: { a: { b: [Object] } } } { parentNode: null }
{ path: [ 'a' ], value: { b: { c: [Array] } } } { parentNode: { path: [], value: { a: [Object] } } }
{ path: [ 'a', 'b' ], value: { c: [ 1, 2 ] } } { parentNode: { path: [ 'a' ], value: { b: [Object] } } }
{ path: [ 'a', 'b', 'c' ], value: [ 1, 2 ] } { parentNode: { path: [ 'a', 'b' ], value: { c: [Array] } } }
{ path: [ 'a', 'b', 'c', 0 ], value: 1 } { parentNode: { path: [ 'a', 'b', 'c' ], value: [ 1, 2 ] } }
{ path: [ 'a', 'b', 'c', 1 ], value: 2 } { parentNode: { path: [ 'a', 'b', 'c' ], value: [ 1, 2 ] } }
function* walkTreeWithTypes(collection) {
const treeWalker = walkTree(collection);
let next;
while ((next = treeWalker.next()) && !next.done) {
const superNode = next.value;
const parentNode = superNode.parentNode;
let node;
if (isArray(superNode.value)) {
node = Object.create({ isArray: true, isBranch: true, parentNode });
} else if (isObject(superNode.value)) {
node = Object.create({ isBranch: true, isObject: true, parentNode });
} else {
node = Object.create({ isLeaf: true, parentNode });
}
node.path = superNode.path;
node.value = superNode.value;
yield node;
}
}
const typesTreeWalker = walkTreeWithTypes({ a: { b: { c: [1, 2] } } });
for (const node of typesTreeWalker) {
console.log(node, node.__proto__);
}
Output:
{ path: [], value: { a: { b: [Object] } } } { isBranch: true, isObject: true, parentNode: null }
{ path: [ 'a' ], value: { b: { c: [Array] } } } { isBranch: true,
isObject: true,
parentNode: { path: [], value: { a: [Object] } } }
{ path: [ 'a', 'b' ], value: { c: [ 1, 2 ] } } { isBranch: true,
isObject: true,
parentNode: { path: [ 'a' ], value: { b: [Object] } } }
{ path: [ 'a', 'b', 'c' ], value: [ 1, 2 ] } { isArray: true,
isBranch: true,
parentNode: { path: [ 'a', 'b' ], value: { c: [Array] } } }
{ path: [ 'a', 'b', 'c', 0 ], value: 1 } { isLeaf: true,
parentNode: { path: [ 'a', 'b', 'c' ], value: [ 1, 2 ] } }
{ path: [ 'a', 'b', 'c', 1 ], value: 2 } { isLeaf: true,
parentNode: { path: [ 'a', 'b', 'c' ], value: [ 1, 2 ] } }