繁体   English   中英

从 Angular 中的平面数组创建树数组

[英]Create a Tree array from a flat array in Angular

我需要制作一个树数组以在 primeng 树组件中使用,但我从后端收到一个平面数组。

我收到的例子:

{name: 'teste', previousName: 'fathername', showOrder: '1', preferredOrder: '2'},
{name: 'teste 2', previousName: 'fathername', showOrder: '1', preferredOrder: '2'},
{name: 'teste 3', previousName: 'teste', showOrder: '1', preferredOrder: '2'},
{name: 'teste 4', previousName: 'teste', showOrder: '1', preferredOrder: '2'},
{name: 'teste 5', previousName: 'teste 3', showOrder: '1', preferredOrder: '2'},
{name: 'teste 6', previousName: 'teste 5', showOrder: '1', preferredOrder: '2'},
]

我需要转换:

[
{label: 'name', data: 'object origial', draggable: true, droppable: true, children: []}
]

首先,我尝试用这个 function 在我需要的对象中创建原始数组

makeTreeNode(array) {
  let arrTreeNode = []
  let treeNodeObj;
  array.filter(element => {
   treeNodeObj = new tree() //tree is a class with the properties that I desire
   treeNodeObj.label = element.name
   treeNodeObj.data = element
   treeNodeObj.draggable = true
   treeNodeObj.droppable = true
   arrTreeNode.push(treeNodeObj)
})

这行得通,但我不知道如何读取这个新数组,使以前的名称等于名称的 de object 放入子项中。

任何人都可以帮助我吗???

具体来说,我会这样定义你的数组

const arr: Data[] = [
  { name: 'teste', previousName: 'fathername', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 2', previousName: 'fathername', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 3', previousName: 'teste', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 4', previousName: 'teste', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 5', previousName: 'teste 3', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 6', previousName: 'teste 5', showOrder: '1', preferredOrder: '2' },
]

其中Data是以下接口:

interface Data {
  name: string,
  previousName: string,
  showOrder: string;
  preferredOrder: string
}

目标是使用以下调用签名实现makeTreeNode() function:

declare function makeTreeNode(array: Data[]): Tree<Data>[];

其中Tree通用的 class,例如

class Tree<T> {
  constructor(
    public label: string,
    public data: T,
    public children: Tree<T>[] = [],
    public draggable = true,
    public droppable = true
  ) { }
}

这是一种可能的方法:

function makeTreeNode(array: Data[]) {

  // keep a mapping from node name to node:
  const nodeMap: Record<string, Tree<Data>> = {};
  array.forEach(element => 
    nodeMap[element.name] = new Tree(element.name, element)
  );
  
  // populate the children
  array.forEach(element =>
    nodeMap[element.previousName]?.children.push(nodeMap[element.name])
  );

  // return only the nodes without a parent
  return Object.values(nodeMap).filter(n => 
    !(n.data.previousName in nodeMap)
  );
  
}

分为三个步骤:

  • 对于每个Data元素,创建一个相应的Tree<Data>节点(带有一个空的children数组)并将其放在nodeMap中与元素name对应的键处。 这使我们以后可以轻松地按名称查找节点。

  • 对于每个Data元素,在nodeMap中找到对应的节点,并将其推送到与其parent名称对应的节点的children数组中。 完成后,所有节点的children节点 arrays 将被完全填充。

  • nodeMap的值中过滤Tree<Data>元素的数组,以便我们只保留nodeMap中没有父元素的那些元素。 这是一个根元素数组,我们返回的就是这个数组。


让我们测试一下:

function displayTreeNodes(array: Tree<Data>[]): string {
  return "[" + array.map(t => t.label + ": " + displayTreeNodes(t.children)).join(", ") + "]"
}
console.log(displayTreeNodes(rootNodes));
// "[teste: [teste 3: [teste 5: [teste 6: []]], teste 4: []], teste 2: []]" 

看起来不错,我们现在有一个包含两个根节点的树结构。


请注意,可以改进makeTreeNode()的实现以避免通过数组的额外循环,方法是将最后两个步骤合并为一个:

// populate children and return array
const ret: Tree<Data>[] = [];
array.forEach(element =>
  (nodeMap[element.previousName]?.children ?? ret).push(nodeMap[element.name])
)
return ret;

但我展示了原始版本,因为它更清楚地展示了概念。

游乐场代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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