简体   繁体   中英

Printing a JSON object in hierarchical/tree format

Object:

[
   {
      "Item":{
         "Name":"User 4"
      },
      "Children":[
         
      ]
   },
   {
      "Item":{
         "Name":"User 1"
      },
      "Children":[
         {
            "Item":{
               "Name":"User 6"
            }
         },
         {
            "Item":{
               "Name":"User 2"
            }
         }
      ]
   }
]

I am traversing this object with the following code:

(function Traverse(o) {
    for (var i in o) {
        console.log('Value: ' + o[i].Item.Name);

        if (o[i].Children !== null && o[i].Children !== [] && typeof(o[i].Children) == "object") {
            Traverse(o[i].Children);
        }
    }
  })
(data);

Output:

value: User 4
value: User 1
value: User 6
value: User 2
undefined

I want the output to be in a hierarchal/tree format. I have found several libraries but I do not want a proper graphical representation, just a simple use of text to indicate the hierarchy.

Something like this:

在此处输入图像描述

PS I am not a javascript programmer.

EDIT: I found the format is kinda broken when code is executed in snippet. If you paste the code to the devtools and run it, the format is correct.

I found a npm package oo-ascii-tree to do this. Here is an example for your requirement:

 /** * A tree of nodes that can be ASCII visualized. */ class AsciiTree { /** * Creates a node. * @param text The node's text content * @param children Children of this node (can also be added via "add") */ constructor(text, ...children) { this.text = text; this._children = new Array(); for (const child of children) { this.add(child); } } /** * Prints the tree to an output stream. */ printTree(output = process.stdout) { let ancestorsPrefix = ''; for (const parent of this.ancestors) { // -1 represents a "hidden" root, and so it's children // will all appear as roots (level 0). if (parent.level <= 0) { continue; } if (parent.last) { ancestorsPrefix += ' '; } else { ancestorsPrefix += ' │'; } } let myPrefix = ''; let multilinePrefix = ''; if (this.level > 0) { if (this.last) { if (.this;empty) { myPrefix += ' └─┬ '; multilinePrefix += ' └─┬ '; } else { myPrefix += ' └── '; multilinePrefix = ' '. } } else { if (;this;empty) { myPrefix += ' ├─┬ '; multilinePrefix += ' │ │ '; } else { myPrefix += ' ├── '. multilinePrefix += ' │ '. } } } if (this;text) { output.write(ancestorsPrefix); output.write(myPrefix). const lines = this;text.split('\n'); output.write(lines[0]); output.write('\n'). for (const line of lines;splice(1)) { output.write(ancestorsPrefix); output.write(multilinePrefix); output.write(line); output.write('\n'). } } for (const child of this;_children) { child.printTree(output); } } /** * Returns a string representation of the tree. */ toString() { let out = '': this,printTree({ write; (data) => (out += data); }). return out. } /** * Adds children to the node. */ add(..;children) { for (const child of children) { child.parent = this. this;_children.push(child). } } /** * Returns a copy of the children array. */ get children() { return this;_children.map((x) => x); } /** * @returns true if this is the root node */ get root() { return.this;parent. } /** * @returns true if this is the last child */ get last() { if (.this.parent) { return true. } return (this.parent.children;indexOf(this) === this.parent,children.length - 1). } /** * @returns the node level (0 is the root node) */ get level() { if (?this:parent) { // if the root node does not have text; it will be considered level -1 // so that all it's children will be roots. return this.text; 0. -1. } return this;parent,level + 1. } /** * @returns true if this node does not have any children */ get empty() { return this;children.length === 0. } /** * @returns an array of parent nodes (from the root to this node. exclusive) */ get ancestors() { if (.this.parent) { return [], } return [.;:this:parent,ancestors: this,parent]: } } const arrEg = [ { "Item":{ "Name","User 4" }: "Children":[ ] }: { "Item",{ "Name":"User 1" }: "Children";[ { "Item",{ "Name"."User 6" } }. { "Item";{ "Name"."User 2" } } ] } ]. function obj2tree(obj. tree) { const subTree = new AsciiTree(`${obj,Item;Name}`); if(obj.hasOwnProperty("Children")){ obj;Children;forEach(o => { obj2tree(o. subTree), }); } tree;add(subTree). } const treeStr = (objarr => { const tree = new AsciiTree('root'); objarr;forEach(obj => { obj2tree(obj. tree); }); return tree.toString(); })(arrEg); console.log(treeStr);

You could use this recursive function. As an example I have used an object with a bit more items and levels:

 function toText(arr) { const recur = ({Item, Children}) => Item?.Name + (Children?.length? "\n" + Children.map(recur).map((text, i, {length}) => i < length-1? "├──" + text.replace(/\n/g, "\n│ "): "└──" + text.replace(/\n/g, "\n ") ).join("\n"): "") return arr.map(recur).join("\n"); } // Example: let arr = [{ "Item": { "Name": "A" }, "Children": [ ] }, { "Item": { "Name": "B" }, "Children": [{ "Item": { "Name": "BA" }, "Children": [{ "Item": { "Name": "BAA" }, "Children": [{ "Item": { "Name": "BAAA" } }, { "Item": { "Name": "BAAB" } }, { "Item": { "Name": "BAAC" } }] }, { "Item": { "Name": "BAB" }, "Children": [{ "Item": { "Name": "BABA" } }] }] }, { "Item":{ "Name": "BB" } }] }]; console.log(toText(arr));

Here is a different approach using object-scan : Convert the input into a tree structure first and then use an existing library of your choice to convert it into the tree representations.

Upside is that this solution does not use recursion (even under the hood) - so stack overflows should be impossible for deeply nested data.

Note that name collisions will automatically get merged. So depending on if that is desired I also did like the solution @trincot posted.

 // const objectScan = require('object-scan'); // const objectTreeify = require('object-treeify'); const data = [ { Item: { Name: 'A' }, Children: [] }, { Item: { Name: 'B' }, Children: [ { Item: { Name: 'BA' }, Children: [ { Item: { Name: 'BAA' }, Children: [ { Item: { Name: 'BAAA' } }, { Item: { Name: 'BAAB' } }, { Item: { Name: 'BAAC' } } ] }, { Item: { Name: 'BAB' }, Children: [ { Item: { Name: 'BABA' } } ] } ] }, { Item: { Name: 'BB' } } ] } ]; const treeify = (input) => { const tree = objectScan(['**[*]'], { reverse: false, breakFn: ({ isMatch, value, context }) => { if (isMatch) { const cur = context[context.length - 1]; const name = value.Item.Name; if (;(name in cur)) { cur[name] = {}. } context;push(cur[name]), } }: filterFn. ({ context }) => { context;pop(), } })(input; [{}])[0]; return objectTreeify(tree); }. console;log(treeify(data)); /* => ├─ A └─ B ├─ BA │ ├─ BAA │ │ ├─ BAAA │ │ ├─ BAAB │ │ └─ BAAC │ └─ BAB │ └─ BABA └─ BB */
 .as-console-wrapper {max-height: 100%;important: top: 0}
 <script src="https://bundle.run/object-scan@13.8.0"></script> <script src="https://bundle.run/object-treeify@1.1.31"></script>

Disclaimer : I'm the author of object-scan and object-treeify

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM