You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular项目中如何将列表转换为Primeng TreeSelect所需树形结构?

如何将扁平列表转换为PrimeNG TreeSelect所需的树形结构?

在Angular项目中,需要把扁平的合同列表转换为三层树形结构(国家 -> 产品类型 -> 交付周期),以适配PrimeNG的TreeSelect组件。

初始数据结构

initialDataStructure = [
  { "country": "GERMANY", "productType": "BASE", "deliveryPeriod": "MONTH"},
  { "country": "GERMANY", "productType": "BASE", "deliveryPeriod": "YEAR"},
  { "country": "GERMANY", "productType": "PEAK", "deliveryPeriod": "MONTH"},
  { "country": "GERMANY", "productType": "PEAK", "deliveryPeriod": "YEAR"},
  
  { "country": "AUSTRIA", "productType": "BASE", "deliveryPeriod": "MONTH"},
  { "country": "AUSTRIA", "productType": "BASE", "deliveryPeriod": "YEAR"},
  { "country": "AUSTRIA", "productType": "PEAK", "deliveryPeriod": "MONTH"},
  { "country": "AUSTRIA", "productType": "PEAK", "deliveryPeriod": "YEAR"},
]

尝试过的代码(未达预期)

for (var i = 0, len = this.listContracts.length, p; i < len; i++) { // faster than .forEach
  p = this.listContracts[i];

  if (this.grouped[p.country] === undefined) // twice faster then hasOwnProperty
    this.grouped[p.country] ={}

  if (this.grouped[p.country][p.productType] === undefined)
    this.grouped[p.country][p.productType] ={}

  if (this.grouped[p.country][p.productType][p.deliveryPeriod] === undefined)
    this.grouped[p.country][p.productType][p.deliveryPeriod]=[]

  this.grouped[p.country][p.productType][p.deliveryPeriod].push(p); //  groupby is HERE xD
}

期望的树形结构

expectedDataStructure =  [
  {
    label: "GERMANY",
    children: [
      {
        label: "BASE",
        children: [
          {
            label: "MONTH",
            children: [{ "country": "GERMANY", "productType": "BASE", "deliveryPeriod": "MONTH"}]
          },
          {
            label: "YEAR",
            children: [{ "country": "GERMANY", "productType": "BASE", "deliveryPeriod": "YEAR"}]
          }
        ]
      },
      {
        label: "PEAK",
        children: [
          {
            label: "MONTH",
            children: [{ "country": "GERMANY", "productType": "PEAK", "deliveryPeriod": "MONTH"}]
          },
          {
            label: "YEAR",
            children: [{ "country": "GERMANY", "productType": "PEAK", "deliveryPeriod": "YEAR"}]
          }
        ]
      },
    ]
  },
  {
    label: "AUSTRIA",
    children: [
      {
        label: "BASE",
        children: [
          {
            label: "MONTH",
            children: [{ "country": "AUSTRIA", "productType": "BASE", "deliveryPeriod": "MONTH"}]
          },
          {
            label: "YEAR",
            children: [{ "country": "AUSTRIA", "productType": "BASE", "deliveryPeriod": "YEAR"}]
          }
        ]
      },
      {
        label: "PEAK",
        children: [
          {
            label: "MONTH",
            children: [{ "country": "AUSTRIA", "productType": "PEAK", "deliveryPeriod": "MONTH"}]
          },
          {
            label: "YEAR",
            children: [{ "country": "AUSTRIA", "productType": "PEAK", "deliveryPeriod": "YEAR"}]
          }
        ]
      },
    ]
  },
]

解决方案

方法1:先分组再转换结构

先通过reduce按层级分组,再将嵌套对象转换为TreeSelect需要的label+children结构,逻辑清晰易读:

function convertToTreeStructure(data: any[]): any[] {
  // 第一步:按国家->产品类型->交付周期分组
  const grouped = data.reduce((acc, item) => {
    if (!acc[item.country]) acc[item.country] = {};
    if (!acc[item.country][item.productType]) acc[item.country][item.productType] = {};
    if (!acc[item.country][item.productType][item.deliveryPeriod]) {
      acc[item.country][item.productType][item.deliveryPeriod] = [];
    }
    acc[item.country][item.productType][item.deliveryPeriod].push(item);
    return acc;
  }, {});

  // 第二步:将分组对象转换为树形结构
  return Object.entries(grouped).map(([country, productTypes]) => ({
    label: country,
    children: Object.entries(productTypes).map(([productType, deliveryPeriods]) => ({
      label: productType,
      children: Object.entries(deliveryPeriods).map(([period, items]) => ({
        label: period,
        children: items
      }))
    }))
  }));
}

// 使用示例
const treeData = convertToTreeStructure(this.initialDataStructure);

方法2:一次遍历直接构建树形

直接在遍历过程中查找或创建节点,减少一次转换步骤,数据量较大时更高效:

function convertToTreeStructureDirectly(data: any[]): any[] {
  const tree: any[] = [];

  data.forEach(item => {
    // 查找或创建国家节点
    let countryNode = tree.find(node => node.label === item.country);
    if (!countryNode) {
      countryNode = { label: item.country, children: [] };
      tree.push(countryNode);
    }

    // 查找或创建产品类型节点
    let productNode = countryNode.children.find(node => node.label === item.productType);
    if (!productNode) {
      productNode = { label: item.productType, children: [] };
      countryNode.children.push(productNode);
    }

    // 查找或创建交付周期节点
    let periodNode = productNode.children.find(node => node.label === item.deliveryPeriod);
    if (!periodNode) {
      periodNode = { label: item.deliveryPeriod, children: [] };
      productNode.children.push(periodNode);
    }

    // 添加当前条目到节点
    periodNode.children.push(item);
  });

  return tree;
}

// 使用示例
const treeData = convertToTreeStructureDirectly(this.initialDataStructure);

可选:TypeScript类型支持

如果需要强类型约束,可以定义树形节点接口:

interface TreeNode {
  label: string;
  children: (TreeNode | Record<string, any>)[];
}

内容的提问来源于stack exchange,提问作者Ahmed Z.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 23:35:22