Search code examples
javascripttreetreeview

Create a tree from a list of strings - javascript


I have an array of objects and I want to create a view tree. But I have a problem with creating. My name Test1/Test2 and Test1/Test2/Test3 (id 147) was missed in final tree.

My snippet:

let arr = [{id: 145, name: "Test1/Test2", public: false},
{id: 146, name: "Test1/Test2/Test3", public: false},
{id: 147, name: "Test1/Test2/Test3", public: false},
{id: 148, name: "Test1/Test2/Test4", public: false}];
let result = [];
let level = { result };

arr.forEach((path) => {
  path.name.split("/").reduce((r, name, i, a) => {
    if (!r[name]) {
      r[name] = { result: [], id: path.id };
      r.result.push({ name, children: r[name].result });
     }
     return r[name];
    }, level);
});

console.log(result)

Expected result:

[
      {
        name: "Test1",
        children: [
          {
            name: "Test2",
            children: [],
            id: 145
          },
          {
            name: "Test2",
            children: [
              {
                name: "Test3",
                children: [],
                id: 146
              },
              {
                name: "Test3",
                children: [],
                id: 147
              },
              {
                name: "Test4",
                children: [],
                id: 148
              },
            ],
          },
        ],
      },
    ];

Solution

  • If I understand well, the number of leaves in your tree should equal the number entries in the input array. So a leaf would never get any children. This is what made you give "Test1" two children, even though all paths have "Test2" as the next part: one child for a leaf, and another functioning as internal node.

    By consequence, leaves don't really need a children property, as that children array would always remain empty.

    It is clear that the last element of a path needs to be processed a bit differently. That part should always result in the creation of a new node in the tree. The other parts can reuse a (non-leaf) node, if one is available.

    This leads to the following change in your code:

    let arr = [
        {id: 145, name: "Test1/Test2", public: false},
        {id: 146, name: "Test1/Test2/Test3", public: false},
        {id: 147, name: "Test1/Test2/Test3", public: false},
        {id: 148, name: "Test1/Test2/Test4", public: false}
    ];
    let result = [];
    let level = { result };
    
    arr.forEach(({name, id}) => { // destructure
      let parts = name.split("/");
      name = parts.pop(); // don't pass the last part through reducer
      parts.reduce((r, name, i) => {
        if (!r[name]) {
          r[name] = { result: [] };
          r.result.push({ name, children: r[name].result });
         }
         return r[name];
      }, level).result.push({ name, id }); // add last part here
    });
    
    console.log(result);