admin管理员组文章数量:1317894
I want to store the "node indentation string" for each object, something like this:
foo
┣bar
┃┗baz
┃ ┗qux
┃ ┣quux
┃ ┗corge
┣fizz
┗buzz
Given data for each object:
objects = [
{'id':1,'parent_id':null, 'name':'foo'}
{'id':2,'parent_id':1, 'name':'bar'}
];
Note that I don't want to print anything, I just want to work out the indent
as an array of characters for each object:
{'id':6,'parent_id':4, 'name':'corge', 'indent':['┃',' ',' ','┗']}
So far I can only indent them with spaces but no 'pipes' and I am stumped at ing up with a solution. Any help?
I am using JS with Angular if it helps.
EDIT: As requested the code I have so far. I didn't post this at first because I felt that it's a wrong foundation/approach to build on. How it works is pretty trivial: for each object, count it's ancestors and add " "
's accordingly.
// go through all our objects and set their indent strings
setIndents = function()
{
for (var x in objects) {
var o = objects[x];
o.nodes = [];
// push space character for amount of ancestors
numParents = countParents(o, 0);
for (var i = 0; i < numParents; i++)
o.nodes.push(" ");
}
};
// recursively counts how many ancestors until we hit the root
countParents = function(current, count)
{
if (current.parent_id !== null) {
for (var x in objects) {
if (objects[x].id == current.parent_id) {
current = objects[x]; //set as new current
count++;
break;
}
}
return countParents(current, count);
} else {
return count;
}
};
I want to store the "node indentation string" for each object, something like this:
foo
┣bar
┃┗baz
┃ ┗qux
┃ ┣quux
┃ ┗corge
┣fizz
┗buzz
Given data for each object:
objects = [
{'id':1,'parent_id':null, 'name':'foo'}
{'id':2,'parent_id':1, 'name':'bar'}
];
Note that I don't want to print anything, I just want to work out the indent
as an array of characters for each object:
{'id':6,'parent_id':4, 'name':'corge', 'indent':['┃',' ',' ','┗']}
So far I can only indent them with spaces but no 'pipes' and I am stumped at ing up with a solution. Any help?
I am using JS with Angular if it helps.
EDIT: As requested the code I have so far. I didn't post this at first because I felt that it's a wrong foundation/approach to build on. How it works is pretty trivial: for each object, count it's ancestors and add " "
's accordingly.
// go through all our objects and set their indent strings
setIndents = function()
{
for (var x in objects) {
var o = objects[x];
o.nodes = [];
// push space character for amount of ancestors
numParents = countParents(o, 0);
for (var i = 0; i < numParents; i++)
o.nodes.push(" ");
}
};
// recursively counts how many ancestors until we hit the root
countParents = function(current, count)
{
if (current.parent_id !== null) {
for (var x in objects) {
if (objects[x].id == current.parent_id) {
current = objects[x]; //set as new current
count++;
break;
}
}
return countParents(current, count);
} else {
return count;
}
};
Share
Improve this question
edited Feb 26, 2014 at 13:06
tiffanyhwang
asked Feb 26, 2014 at 10:13
tiffanyhwangtiffanyhwang
1,4535 gold badges18 silver badges28 bronze badges
2
- Would you also add your existing code that creates the indents? This will allow for suggestions on how to improve it – stackh34p Commented Feb 26, 2014 at 11:11
- @IvayloSlavov yes, absolutely. – tiffanyhwang Commented Feb 26, 2014 at 11:44
3 Answers
Reset to default 7As @JBCP pointed out (see ments) there is a serious flaw in my original code that would break the whole thing if the initial order was anything but perfect.
So here's an updated version, the order of elements can now be random (it still plays a role in such that it indirectly defines the children order, but the tree-structure will be correct).
I also split the functions so that they can be better configured. For example treeIndent
now expects a node branch produced by treeify
. (Note: the shuffle
function is just there to test the order independence)
'use strict';
/**
* @see https://bost.ocks/mike/shuffle/
*
* @param array
* @returns {*}
*/
function shuffle(array) {
var m = array.length, t, i;
// While there remain elements to shuffle…
while (m) {
// Pick a remaining element…
i = Math.floor(Math.random() * m--);
// And swap it with the current element.
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
function treeify(flat) {
var map = { __root__: { children: [] }};
flat.forEach(function (node) {
var
parentId = node.parent_id || '__root__',
id = node.id;
// init parent
if (!map.hasOwnProperty(parentId)) {
map[parentId] = { element: null, children: [] };
}
// init self
if (!map.hasOwnProperty(id)) {
map[id] = { element: null, children: [] };
}
map[id].element = node;
map[parentId].children.push(map[id]);
});
return map.__root__.children;
}
function treeIndent(branch, cfg, decorator, indent)
{
indent = indent || [];
branch.forEach(function (node, i) {
decorator(node.element, indent.concat(
i === branch.length - 1 ? cfg.isLastChild : cfg.hasNextSibling
));
treeIndent(node.children, cfg, decorator, indent.concat(
i === branch.length - 1 ? cfg.ancestorIsLastChild : cfg.ancestorHasNextSibling
));
});
}
var input = [
{ id: 1, parent_id: null, name: 'root' },
{ id: 2, parent_id: 1, name: 'bar' },
{ id: 5, parent_id: 2, name: 'baz' },
{ id: 6, parent_id: 5, name: 'qux' },
{ id: 7, parent_id: 6, name: 'quux' },
{ id: 8, parent_id: 6, name: 'corge' },
{ id: 9, parent_id: 2, name: 'but' },
{ id: 3, parent_id: 1, name: 'fizz' },
{ id: 4, parent_id: 1, name: 'buzz' }
];
var log = document.getElementById('log');
treeIndent(treeify(shuffle(input)), {
hasNextSibling: '├',
isLastChild: '└',
ancestorHasNextSibling: '│',
ancestorIsLastChild: ' '
}, function (element, indent) {
log.innerHTML += indent.join(' ') + ' ' + element.name + "\n";
});
<pre id="log"></pre>
Old answer (broken!):
try the following:
function makeTree(flat) {
var map = { __root__: { children: [] }};
flat.forEach(function (node) {
var
parentId = node.parent_id || '__root__',
id = node.id;
// init parent
if (!map.hasOwnProperty(parentId)) {
map[parentId] = { children: [] };
}
// init self
if (!map.hasOwnProperty(id)) {
map[id] = { children: [] };
}
map[id].element = node;
map[parentId].children.push(map[id]);
});
return map.__root__.children;
}
function injectTreeIndent(input) {
var
levelMap = [],
indicators = {
hasNextSibling: '┣',
isLastChild: '┗',
ancestorHasNextSibling: '┃',
ancestorIsLastChild: ' '
}
;
// apply `indent`
(function traverse(branch, depth) {
branch.forEach(function (node, idx) {
node.element.indent = levelMap.map(function (ancestor) {
return ancestor === indicators.hasNextSibling ? indicators.ancestorHasNextSibling : indicators.ancestorIsLastChild;
});
// if (depth > 0) { // unment this, if root elements should have no indentation
node.element.indent.push(
levelMap[depth] = branch.length - 1 > idx ? indicators.hasNextSibling : indicators.isLastChild
);
// }
traverse(node.children, depth + 1);
levelMap.pop();
});
}(makeTree(input), 0));
}
var input = [
{ id: 1, parent_id: null, name: 'foo' },
{ id: 2, parent_id: 1, name: 'bar' },
{ id: 5, parent_id: 2, name: 'baz' },
{ id: 6, parent_id: 5, name: 'qux' },
{ id: 7, parent_id: 6, name: 'quux' },
{ id: 8, parent_id: 6, name: 'corge' },
{ id: 3, parent_id: 1, name: 'fizz' },
{ id: 4, parent_id: 1, name: 'buzz' }
];
injectTreeIndent(input);
makeTree
is used to optain a nested structure derived from the given flat data.injectTreeIndent
then traverses that nested structure to inject the required indent informatoin.
demo: http://jsfiddle/6R7wf/1/
demo with root elements having no indenation: http://jsfiddle/zMY7v/
After for (var i = 0; i < numParents; i++) o.nodes.push(" ");
, try
if (o.nodes.length === 1)
o.nodes[0] = "┣";
else if (o.nodes.length > 1) {
o.nodes[0] = "┃";
o.nodes[o.nodes.length - 1] = "┗";
}
const addIndents = objects => {
objects.forEach(o => o.children = objects.filter(o2 => o2.parent_id === o.id));
const roots = objects.filter(o => !objects.some(o2 => o2.children.includes(o)));
roots.forEach(o => addTree(o));
};
const PFXs = { true: { true: " ", false: "┃" }, false: { true: "┗", false: "┣" }};
const addTree = (t, levels = []) => {
t.indent = levels.map((p, i) => PFXs[i < levels.length - 1][p]);
t.children?.forEach((x, i) => addTree(x, [...levels, i === t.children.length-1]));
};
const objects = [
{id: 1, parent_id: null, name: "foo"},
{id: 2, parent_id: 1, name: "bar"},
{id: 3, parent_id: 2, name: "baz"},
{id: 4, parent_id: 3, name: "qux"},
{id: 5, parent_id: 4, name: "quux"},
{id: 6, parent_id: 4, name: "corge"},
{id: 7, parent_id: 1, name: "fizz"},
{id: 8, parent_id: 1, name: "buzz"},
];
addIndents(objects);
objects.forEach(o => console.log(o.indent.join("") + o.name))
本文标签:
版权声明:本文标题:javascript - Printing a tree structure in a list like manner, storing the indent strings (which consist of┣, ┃, ┗ ) - Stack Over 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742036625a2417332.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论