Skip to content

Instantly share code, notes, and snippets.

@cheton
Last active March 21, 2016 16:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cheton/21d4272d5e32505ff90f to your computer and use it in GitHub Desktop.
Save cheton/21d4272d5e32505ff90f to your computer and use it in GitHub Desktop.
Flat Tree
'use strict';
import _ from 'lodash';
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
const isArray = Array.isArray;
const extend = (target, ...sources) => {
sources.forEach((source) => {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
});
return target;
};
class FlatTree {
isFolded(node = {}) {
const { state = {} } = node;
const { isFolded = false } = state;
return !!isFolded;
}
flatten(tree = []) {
let flatten = [];
let stack = [];
let current = null;
let depth = 0;
let index = 0;
if (isArray(tree)) { // Array format
let root = { _path: '', _depth: depth, _parent: null, children: tree };
stack.push([root, depth, index]);
} else { // Object format
let root = extend({}, tree, { _path: '', _depth: depth, _parent: null });
flatten.push(root);
stack.push([root, depth, index]);
}
while (stack.length > 0) {
[current, depth, index] = stack.pop();
if (this.isFolded(current)) {
continue;
}
while (index < current.children.length) {
const node = current.children[index];
node._path = current._path + '.' + index;
node._depth = depth + 1;
node._parent = current;
flatten.push(node);
++index;
if (this.isFolded(node)) {
continue;
}
if (node.children) {
// Push back parent node to the stack that will be able to continue
// the next iteration once all the child nodes of the current node
// have been completely explored.
stack.push([current, depth, index]);
index = 0;
depth = depth + 1;
current = node;
}
}
}
return flatten;
}
}
const ft = new FlatTree();
const tree = {
label: 'ROOT',
state: {
isFolded: false
},
children: [
{
label: 'A',
props: {
},
state: {
isFolded: false
},
children: [
{
label: 'B',
state: {
isFolded: false
},
children: [
{
label: 'C',
state: {
isFolded: true
},
children: [
{
label: 'D'
}
]
}
]
},
{
label: 'E',
children: [
{
label: 'F',
children: [
{
label: 'G'
}
]
}
]
}
]
},
{
label: 'H',
state: {
isFolded: false
},
children: [
{
label: 'I',
children: []
}
]
}
]
};
bin.log('');
bin.log('// Example #1: FlatTree - A flat list view');
ft.flatten(tree).forEach((item, index) => {
const { _depth, _path, label = '', state = {}, children = [] } = item;
bin.log(JSON.stringify({ _depth, _path, label, folded: !!state.isFolded, children: children.length }));
});
bin.log('');
bin.log('// Example #2: FlatTree with root node');
ft.flatten(tree).forEach((item, index) => {
const { _depth, _path, label = '', state = {}, children = [] } = item;
let padding = _.pad('', _depth, ' ');
if (item.state && item.state.isFolded){
padding += '[+] ';
} else if (item.children && item.children.length > 0) {
padding += '[-] ';
} else {
padding += '(0) ';
}
bin.log(padding + JSON.stringify({ _path, label, folded: !!state.isFolded, children: children.length }));
});
bin.log('');
bin.log('// Example #3: FlatTree without root node');
ft.flatten(tree.children).forEach((item, index) => {
const { _depth, _path, label = '', state = {}, children = [] } = item;
let padding = _.pad('', _depth - 1, ' ');
if (item.state && item.state.isFolded){
padding += '[+] ';
} else if (item.children && item.children.length > 0) {
padding += '[-] ';
} else {
padding += '(0) ';
}
bin.log(padding + JSON.stringify({ _path, label, folded: !!state.isFolded, children: children.length }));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment