软件教程 2025年08月6日
0 收藏 0 点赞 677 浏览 3155 个字
摘要 :

文章目录 场景示例 思路 完整代码 本文主要讲解关于js将一维数组转换为树状嵌套对象 相关内容,让我们来一起学习下吧! 当开发过程中,遇到一组有层级关系的一维数组……




  • 场景示例
  • 思路
  • 完整代码

本文主要讲解关于js一维数组转换树状嵌套对象 相关内容,让我们来一起学习下吧!

当开发过程中,遇到一组有层级关系的一维数组对象,需要将其处理为有上下级依赖关系的树状对象,遇到这个场景可以将本文的内容做参考使用。

场景示例

本文内容只适合处理像以下有清晰简明的数据结构,复杂凌乱的结构是不支持的。

现在有一份一维数组数据,其中每项有一个属性代表着其级别(权重),其数据如下:

const list = [
  {
    name: \'目录\',
    weight: 1
  },
  {
    name: \'导航一\',
    weight: 2
  },
  {
    name: \'导航-路由一\',
    weight: 3
  },
  {
    name: \'导航-路由二\',
    weight: 3
  },
  {
    name: \'导航二\',
    weight: 2
  },
  {
    name: \'导航二-路由\',
    weight: 3
  }
]

现在的诉求是,将list数组转为树状对象,并能正确的表达其上下级关系。期望结果为:

const result = {
  name: \'目录\',
  weight: 1,
  children: [
    {
      name: \'导航一\',
      weight: 2,
      children: [
        {
          name: \'导航-路由一\',
          weight: 3,
          children: []
        },
        {
          name: \'导航-路由二\',
          weight: 3,
          children: []
        },
      ]
    },
    {
      name: \'导航二\',
      weight: 2,
      children: [
        {
          name: \'导航二-路由\',
          weight: 3,
          children: []
        }
      ]
    }
  ]
}

若是你也有上面场景需求,那就向下划拉划拉吧。

这个场景的使用举一个简单例子:

需要对md文件的h标签内容作为目录或者锚点信息使用。比如掘金是怎么处理md的h标签转为右侧目录的。虽不知具体逻辑,但读取md内容,解析h标签内容,转为层级结构这个过程肯定不会少的。当然这个过程也能使用一些md相关库辅助快速实现。

接下来展开聊下具体思路与代码实现:

思路

首先分析这个list,能直观感受到的就是weight属性是破局的关键点。

但怎么能让它顺顺利利的听话,还需要设置一些规则才行。

比如:

  • weight在循环过程中若是遇到相同或小的值,需要结束循环,因为后面的就属于下一个关系了。
  • weight需要在循环过程中有一个参考项,这个参考项告诉它是否越界了。

完整代码

// 关系解析器 - 适用于关系清晰的数组结构,而且只存在一个最高级,通常是第一项。
class RelationshipParser {
  #dataSource; // 源数据
  #result; // 处理的结果

  /**
   * 
   * @param {array} dataSource // 一维数组源数据, 没用ts编写的话,尽量校验下dataSource
   */
  constructor(dataSource) {
    this.#dataSource = JSON.parse(JSON.stringify(dataSource));
    this.#init();
  }

  getResult() {
    return JSON.parse(JSON.stringify(this.#result))
  }

  #init() {
    const topLevelItem = this.#getTopLevel(); // 通常只有一个最高级
    this.#parseData(this.#dataSource, topLevelItem);
    this.#result = topLevelItem;
  }

  #getTopLevel() {
    const topValue = Math.min(...this.#dataSource.map(item => item.weight));
    return this.#dataSource.find(item => item.weight === topValue);  
  }

  /**
   * 递归解析当前dataSource,组成依赖关系树
   * @param {array} dataSource // 源数据
   * @param {object} currentItem // 当前节点,parseData函数处理的都是他的子级
   */
  #parseData(dataSource, currentItem) {
    currentItem.children = [] // 这个children就是容器,可以修改
    // 起始索引,用于循环使用
    const startIndex = dataSource.findIndex(item => item.weight === currentItem.weight);
    // 当前权重,用于划分当前边界
    const currentWeight = currentItem.weight;
    // 边界,用于划分当前命中范围
    let boundaryDepth = Number.MAX_SAFE_INTEGER;
    // 这里startIndex + 1作为起始索引,是为了只处理currentItem后面的数据
    for (let index = startIndex + 1; index < dataSource.length; index++) {
      const item = dataSource[index];
      // 若当前权重小于等于入参权重,则跳出循环。
      // 如 weigit:3 = weigit: 3, 说明是同级
      // 如 weigit:2 < weigit: 3, 说明没有关系
      // 如 weigit:4 > weigit: 3, 说明是嵌套关系,继续向下处理
      if (item.weight <= currentWeight) {
        break;
      }
      
      // 若当前权重小于等于边界权重,其实就是不是同一个权重就不处理
      // 如 weigit:2 < weight: 10000,说明是第一次命中,将当前项push到 currentItem 内
      // 只有第一次是小于,后面只会处理等于,因为小于在上一拦截了,大于就是越界了不做处理
      if (item.weight <= boundaryDepth) {
        // 递归处理当前项的子级
        this.#parseData(dataSource.slice(index), item);
        // 将当前项push到currentItem
        currentItem.children.push(item);
        boundaryDepth = item.weight;
      }
    }
  }
}

看下使用效果:

// 列表
const list = [
  {
    name: \'目录\',
    weight: 1
  },
  {
    name: \'导航一\',
    weight: 2
  },
  {
    name: \'导航-路由一\',
    weight: 3
  },
  {
    name: \'导航-路由二\',
    weight: 3
  },
  {
    name: \'导航二\',
    weight: 2
  },
  {
    name: \'导航二-路由\',
    weight: 3
  }
]

// 调用

const relationshipParser = new RelationshipParser(list);

console.log(relationshipParser.getResult());
// => 如下
{
    \"name\":\"目录\",
    \"weight\":1,
    \"children\":[
        {
            \"name\":\"导航一\",
            \"weight\":2,
            \"children\":[
                {
                    \"name\":\"导航-路由一\",
                    \"weight\":3,
                    \"children\":[

                    ]
                },
                {
                    \"name\":\"导航-路由二\",
                    \"weight\":3,
                    \"children\":[

                    ]
                }
            ]
        },
        {
            \"name\":\"导航二\",
            \"weight\":2,
            \"children\":[
                {
                    \"name\":\"导航二-路由\",
                    \"weight\":3,
                    \"children\":[

                    ]
                }
            ]
        }
    ]
}

以上就是关于js将一维数组转换为树状嵌套对象 相关的全部内容,希望对你有帮助。欢迎持续关注潘子夜个人博客(www.panziye.com),学习愉快哦!

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/6641.html

管理员

相关推荐
2025-08-06

文章目录 一、Promise基础回顾 二、Promise 与 axios 结合使用场景及方法 (一)直接返回 axios …

269
2025-08-06

文章目录 一、模块初始化时的内部机制 二、常见导出写法的差异分析 (一)写法一:module.exports…

107
2025-08-06

文章目录 一、ResizeObserver详解 (一)ResizeObserver是什么 (二)ResizeObserver的基本用法 …

683
2025-08-06

文章目录 一、前期准备工作 (一)下载相关文件 (二)安装必要工具 二、处理扣子空间生成的文件…

338
2025-08-06

文章目录 一、官方文档 二、自动解包的数据类型 ref对象:无需.value即可访问 reactive对象:保持…

371
2025-08-06

文章目录 一、Hooks的工作原理 二、在if语句中使用Hook会出什么岔子? 三、React官方的Hook使用规…

843
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号