Home > Enterprise >  Fill treeview from custom array of objects. Recursion call
Fill treeview from custom array of objects. Recursion call

Time:04-30

I'm using treeview to display my hierarchical data.

I have following array of objects:

const data = [
      { id: 1, hierarchyid: "/", level: 0, name: "Mhz" },
      { id: 2, hierarchyid: "/2/", level: 1, name: "SMT" },
      { id: 3, hierarchyid: "/3/", level: 1, name: "QC" },
      { id: 4, hierarchyid: "/3/4/", level: 2, name: "Tester" },
      { id: 5, hierarchyid: "/3/5/", level: 2, name: "Operator" },
    ];

I need to fill my treeview with this data. What I did so far:

getTreeItems(node, nodes) {
    const filtered = nodes.filter(
      (n) => n.hierarchyid.includes(node.hierarchyid) && n.level != node.level
    );

    return (
      <TreeItem
        key={node.id}
        nodeId={node.hierarchyid}
        label={node.name}
        onClick={() => onClicked(node)}
      >
        {filtered.length > 0
          ? filtered.map((node) => this.getTreeItems(node, filtered))
          : null}
      </TreeItem>
    );
  }

And rendering:

render() {
    // The data comes from Server
    const data = [
      { id: 1, hierarchyid: "/", level: 0, name: "Mhz" },
      { id: 2, hierarchyid: "/2/", level: 1, name: "SMT" },
      { id: 3, hierarchyid: "/3/", level: 1, name: "QC" },
      { id: 4, hierarchyid: "/3/4/", level: 2, name: "Tester" },
      { id: 5, hierarchyid: "/3/5/", level: 2, name: "Operator" },
    ];
    return (
      <TreeView
        aria-label="file system navigator"
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        sx={{ height: "auto", flexGrow: 1, width: "auto", overflowY: "auto" }}
      >
        {this.getTreeItems(
          { id: 1, hierarchyid: "/", level: 0, name: "Mhz" },
          data
        )}
      </TreeView>
    );
  }
}

This giving me view like:

 Mhz
  SMT
  QC
   Tester
   Operator
  Tester    // they shouldn't be displayed
  Operator // they have already rendered as child under QC

My problem is can not exclude already rendered nodes.

Update

MUI TreeView supports special JSON data for its nodes. So converting array to JSON also solves the problem. Something like that:

const data = {
      id: 1,
      hierarchyid: "/",
      level: 0,
      name: "Mhz",
      children: [
        {
          id: 2,
          hierarchyid: "/2/",
          level: 1,
          name: "SMT"
        },
        {
          id: 3,
          hierarchyid: "/3/",
          level: 1,
          name: "QC",
          children: [
            {
              id: 4,
              hierarchyid: "/3/4/",
              level: 2,
              name: "Tester"
            },
            {
              id: 5,
              hierarchyid: "/3/5/",
              level: 2,
              name: "Operator"
            }
          ]
        }
      ]
    };

the array of objects came from Server, so how can I make this Json from array data?

CodePudding user response:

If we write a quick function to test whether one hierarchy id is the direct descendant of another, then we can use it to write a simple recursive version:

const isChild = (prefix) => ({hierarchyid}) =>
  hierarchyid .startsWith (prefix) 
  && /^[^\/]*\/$/ .test (hierarchyid .slice (prefix .length))

const nest = (xs, prefix = '') => 
  xs .filter (isChild (prefix)) .map ((x, _, __, children = nest (xs, x .hierarchyid)) => ({
    ...x, 
    ... (children .length ? {children} : {})
  }))

const data = [{id: 1, hierarchyid: "/", level: 0, name: "Mhz"}, {id: 2, hierarchyid: "/2/", level: 1, name: "SMT"}, {id: 3, hierarchyid: "/3/", level: 1, name: "QC"}, {id: 4, hierarchyid: "/3/4/", level: 2, name: "Tester"}, {id: 5, hierarchyid: "/3/5/", level: 2, name: "Operator"}]


console .log (nest (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

isChild checks whether the hierarchy id starts with our prefix and if the remainder has only one '/', at the very end.

nest is not as efficient as it might be, as it scans the whole array for each node. But I wouldn't worry about it until I had tens of thousands of entries.

If you don't mind having some empty children arrays on your leaves, it's simpler still:

const nest = (xs, prefix = '') => 
  xs .filter (isChild (prefix)) .map ((x) => ({
    ...x,
    children: nest (xs, x .hierarchyid)
  }))

CodePudding user response:

SO I've looked at the documentation and they've clearly mentioned a way to design your data object in such a way so that the hierarchy can be shown without multiple nodes repeating.

Try going through this once and change your render functions according to the documentation, you can probably get your desired result.

const data: RenderTree = {
  id: "root",
  name: "Mhz",
  children: [
    {
      id: "1",
      name: "SMT"
    },
    {
      id: "3",
      name: "QZ",
      children: [
        {
          id: "4",
          name: "Tester"
        },
        {
          id: "4",
          name: "Operator"
        }
      ]
    }
  ]
};

  • Related