创建一个采用平面数组并转换为树数据结构的递归函数

时间:2019-02-07 05:39:17

标签: javascript tree

因此,我尝试编写一个递归函数,该函数采用带有对象的值,id和其父节点的id的对象的平面数组,并将其转换为树结构,其中该结构的子级为数组节点。子项需要按id排序,如果其null可以是根节点。

试图将函数写入Tree(data)的函数仅应接收数据数组。没有父母,我一直做不到。到目前为止,我所拥有的是一个函数(下面),该函数需要数据和父对象来开始。

输入:

 const tasks = [
  { id: 1, parent: null, value: 'Make breakfast' },
  { id: 2, parent: 1, value: 'Brew coffee' },
  { id: 3, parent: 2, value: 'Boil water' },
  { id: 4, parent: 2, value: 'Grind coffee beans' },
  { id: 5, parent: 2, value: 'Pour water over coffee grounds' }
];

输出:

{
  id: 1,
  parent: null,
  value: 'Make Breakfast',
  children: [
     {
       id: 2,
       parent: 1,
       value: 'Brew coffee',
       children: [
          { id: 3, parent: 2, value: 'Boil water' },
          { id: 4, parent: 2, value: 'Grind coffee beans' },
          { id: 5, parent: 2, value: 'Pour water over coffee grounds'     }
       ]
     }
  ]
}


funciton toTree(data) {
  customtoTree (data, null);
}

function customToTree (data, parent) {
  const out = [];
  data.forEach((obj) => {
    if (obj.parent === parent) {
      const children = customToTree(data,obj.parent);

      if (children.length) {
        obj[children[0]] = children;
      }
      const {id,parent, ...content} = obj;
      out.push(content);
    }
  });
  return out;
}

我真的很想了解有关如何执行此操作的正确逻辑,并对此进行思考,以及如何在不明确给予父母的情况下进行操作。

3 个答案:

答案 0 :(得分:1)

我无法检查更多的测试用例,但这是我很快就能想到的,可以通过您的用例,它看起来不太好,我建议将其用作初始结构,然后在其上构建。另外,我假设任务按父级升序排序,即子代只会出现在任务数组中父代之后

const tasks = [
  { id: 1, parent: null, value: 'Make breakfast' },
  { id: 2, parent: 1, value: 'Brew coffee' },
  { id: 3, parent: 2, value: 'Boil water' },
  { id: 4, parent: 2, value: 'Grind coffee beans' },
  { id: 5, parent: 2, value: 'Pour water over coffee grounds' },
  { id: 6, parent: 5, value: 'Pour water over coffee grounds' },
  { id: 7, parent: 5, value: 'Pour water over coffee grounds' }
];

function Tree() {
  this.root = null;
  // this function makes node root, if root is empty, otherwise delegate it to recursive function
  this.add = function(node) {
    if(this.root == null)
      this.root = new Node(node);
    else
      // lets start our processing by considering root as parent
      this.addChild(node, this.root);
  }

  this.addChild = function(node, parent) {
    // if the provided parent is actual parent, add the node to its children
    if(parent.id == node.parent) {
      parent.children[node.id] = new Node(node);
    } else if(parent.children[node.parent]) {
      // if the provided parent children contains actual parent call addChild with that node
      this.addChild(node, parent.children[node.parent])
    } else if(Object.keys(parent.children).length > 0) {
      // iterate over children and call addChild with each child to search for parent
      for(let p in parent.children) {
        this.addChild(node, parent.children[p]);
      }
    } else {
      console.log('parent was not found');
    }
  }
}

function Node (node) {
  this.id = node.id;
  this.parent = node.parent;
  this.value = node.value;
  this.children = {};
}

const tree = new Tree();

// We are assuming that tasks are sorted in ascending order by parent

for(let t of tasks) {
  tree.add(t);
}

console.log(JSON.stringify(tree.root))

如果您有任何疑问,请告诉我。一起破解吧

答案 1 :(得分:0)

如果您的输入已经按ID排序,并且子节点不能在列表中的父节点之前,那么您可以在一个循环中执行此操作,甚至不需要递归:

 const tasks = [
  { id: 1, parent: null, value: 'Make breakfast' },
  { id: 2, parent: 1, value: 'Brew coffee' },
  { id: 3, parent: 2, value: 'Boil water' },
  { id: 4, parent: 2, value: 'Grind coffee beans' },
  { id: 5, parent: 2, value: 'Pour water over coffee grounds' }
];

const tasksById = Object.create(null);

// abusing filter to do the work of a forEach() 
// while also filtering the tasks down to a list with `parent: null`
const root = tasks.filter((value) => {
  const { id, parent } = value;
  
  tasksById[id] = value;
  
  if(parent == null) return true;
  
  (tasksById[parent].children || (tasksById[parent].children = [])).push(value);
});

console.log("rootNodes", root);
console.log("tasksById", tasksById);
.as-console-wrapper{top:0;max-height:100%!important}

答案 2 :(得分:0)

在面试中我遇到了同样的问题,但是我无法解决。我也很困惑,该函数应该只将数组作为第一个也是唯一的参数。

但是在稍后对其进行了重新设计(以及一个有才华的人的一些很好的建议)之后,我意识到您可以在第一次调用数组时将该函数作为第一个也是唯一的参数,然后在递归调用中传递父函数作为第二个论点。

在函数内部,您只需要检查第二个参数是否未定义即可,如果是,则在数组中搜索您的根对象并将其分配给第二个参数。

所以这是我的解决方案,希望它会更清楚:

MyClass